diff --git a/app/components/chart-selection-tabs.tsx b/app/components/chart-selection-tabs.tsx index 56ab11d83..3d71ba62f 100644 --- a/app/components/chart-selection-tabs.tsx +++ b/app/components/chart-selection-tabs.tsx @@ -29,10 +29,12 @@ import { enableLayouting, getChartConfig, hasChartConfigs, + initChartStateFromChartEdit, isConfiguring, isLayouting, isPublished, isPublishing, + saveChartLocally, useConfiguratorState, } from "@/configurator"; import { ChartTypeSelector } from "@/configurator/components/chart-type-selector"; @@ -329,7 +331,7 @@ export const SaveDraftButton = ({ const { data: config, invalidate: invalidateConfig } = useUserConfig(chartId); const session = useSession(); - const [state] = useConfiguratorState(); + const [state, dispatch] = useConfiguratorState(); const [snack, enqueueSnackbar, dismissSnack] = useLocalSnack(); const [debouncedSnack] = useDebounce(snack, 500); @@ -370,7 +372,15 @@ export const SaveDraftButton = ({ }), variant: "success", }); - replace(`/create/new?edit=${saved.key}`); + const config = await initChartStateFromChartEdit(saved.key); + if (!config) { + return; + } + dispatch({ type: "INITIALIZED", value: config }); + saveChartLocally(saved.key, config); + replace(`/create/${saved.key}`, undefined, { + shallow: true, + }); } else { throw new Error("Could not save draft"); } diff --git a/app/configurator/configurator-state.tsx b/app/configurator/configurator-state.tsx index fb48f49f5..b7d721655 100644 --- a/app/configurator/configurator-state.tsx +++ b/app/configurator/configurator-state.tsx @@ -1532,6 +1532,14 @@ export const initChartStateFromLocalStorage = async ( window.localStorage.removeItem(getLocalStorageKey(chartId)); }; +export const saveChartLocally = (chartId: string, state: ConfiguratorState) => { + window.localStorage.setItem( + getLocalStorageKey(chartId), + JSON.stringify(state) + ); + return; +}; + const ConfiguratorStateProviderInternal = ( props: ConfiguratorStateProviderProps ) => { @@ -1598,24 +1606,16 @@ const ConfiguratorStateProviderInternal = ( switch (state.state) { case "CONFIGURING_CHART": case "LAYOUTING": - if (chartId === "new") { - const chartId = - query.edit && typeof query.edit === "string" + const savedChartId = + chartId === "new" + ? query.edit && typeof query.edit === "string" ? query.edit - : createChartId(); - replace(`/create/${chartId}`); - window.localStorage.setItem( - getLocalStorageKey(chartId), - JSON.stringify(state) - ); - } else { - // Store current state in localstorage - window.localStorage.setItem( - getLocalStorageKey(chartId), - JSON.stringify(state) - ); + : createChartId() + : chartId; + if (chartId === "new") { + replace(`/create/${savedChartId}`); } - + saveChartLocally(savedChartId, state); return; case "PUBLISHING": diff --git a/app/db/config.ts b/app/db/config.ts index 5f226490c..d0b4ea229 100644 --- a/app/db/config.ts +++ b/app/db/config.ts @@ -20,19 +20,19 @@ export const createConfig = async ({ key, data, userId, - publishedState, + published_state, }: { key: string; data: Prisma.ConfigCreateInput["data"]; userId?: User["id"] | undefined; - publishedState: PUBLISHED_STATE; + published_state: PUBLISHED_STATE; }): Promise<{ key: string }> => { return await prisma.config.create({ data: { key, data, user_id: userId, - published_state: publishedState, + published_state, }, }); }; diff --git a/app/login/components/profile-content-tabs.tsx b/app/login/components/profile-content-tabs.tsx index 775444f05..b348a9d95 100644 --- a/app/login/components/profile-content-tabs.tsx +++ b/app/login/components/profile-content-tabs.tsx @@ -90,7 +90,7 @@ export const ProfileContentTabs = (props: ProfileContentTabsProps) => { { const { invalidate: invalidateUserConfigs } = useUserConfigs(); - const updateConfigMut = useMutate(updateConfig); const removeConfigMut = useMutate(removeConfig); const { @@ -375,40 +374,40 @@ const ProfileVisualizationsRow = (props: ProfileVisualizationsRowProps) => { label: t({ id: "login.chart.share", message: "Share" }), iconName: "linkExternal", }, - { - type: "button", - label: - config.published_state === PUBLISHED_STATE.DRAFT - ? t({ - id: "login.chart.actions.publish", - message: `Publish`, - }) - : t({ - id: "login.chart.actions.turn-into-draft", - message: "Turn into draft", - }), - iconName: - updateConfigMut.status === "fetching" ? "loading" : "linkExternal", + // { + // type: "button", + // label: + // config.published_state === PUBLISHED_STATE.DRAFT + // ? t({ + // id: "login.chart.actions.publish", + // message: `Publish`, + // }) + // : t({ + // id: "login.chart.actions.turn-into-draft", + // message: "Turn into draft", + // }), + // iconName: + // updateConfigMut.status === "fetching" ? "loading" : "linkExternal", - onClick: async () => { - await updateConfigMut.mutate({ - key: config.key, - user_id: userId, - data: { - ...config.data, - state: "PUBLISHING", - }, - published_state: - config.published_state === PUBLISHED_STATE.DRAFT - ? PUBLISHED_STATE.PUBLISHED - : PUBLISHED_STATE.DRAFT, - }); - invalidateUserConfigs(); - }, - onSuccess: () => { - invalidateUserConfigs(); - }, - }, + // onClick: async () => { + // await updateConfigMut.mutate({ + // key: config.key, + // user_id: userId, + // data: { + // ...config.data, + // state: "PUBLISHING", + // }, + // published_state: + // config.published_state === PUBLISHED_STATE.DRAFT + // ? PUBLISHED_STATE.PUBLISHED + // : PUBLISHED_STATE.DRAFT, + // }); + // invalidateUserConfigs(); + // }, + // onSuccess: () => { + // invalidateUserConfigs(); + // }, + // }, { type: "button", label: t({ id: "login.chart.rename", message: "Rename" }), @@ -443,14 +442,11 @@ const ProfileVisualizationsRow = (props: ProfileVisualizationsRowProps) => { return sortBy(actions, (x) => x.priority); }, [ - config.data, config.key, config.published_state, invalidateUserConfigs, openRename, removeConfigMut, - updateConfigMut, - userId, ]); const chartTitle = React.useMemo(() => { diff --git a/app/pages/api/config-create.ts b/app/pages/api/config-create.ts index 75da64b09..87709c303 100644 --- a/app/pages/api/config-create.ts +++ b/app/pages/api/config-create.ts @@ -1,24 +1,8 @@ -import { getServerSession } from "next-auth"; - -import { createConfig } from "@/db/config"; - -import { api } from "../../server/nextkit"; - -import { nextAuthOptions } from "./auth/[...nextauth]"; +import ConfigController from "@/server/config-controller"; +import { api } from "@/server/nextkit"; const route = api({ - POST: async ({ req, res }) => { - const session = await getServerSession(req, res, nextAuthOptions); - const userId = session?.user?.id; - const { data, publishedState } = req.body; - - return await createConfig({ - key: data.key, - data, - userId, - publishedState: publishedState, - }); - }, + POST: ConfigController.create, }); export default route; diff --git a/app/pages/api/config-remove.ts b/app/pages/api/config-remove.ts index b53f1e711..d0d7c2b2f 100644 --- a/app/pages/api/config-remove.ts +++ b/app/pages/api/config-remove.ts @@ -1,24 +1,9 @@ -import { getServerSession } from "next-auth"; - -import { getConfig, removeConfig } from "@/db/config"; +import ConfigController from "@/server/config-controller"; import { api } from "../../server/nextkit"; -import { nextAuthOptions } from "./auth/[...nextauth]"; - const route = api({ - POST: async ({ req, res }) => { - const session = await getServerSession(req, res, nextAuthOptions); - const sessionUserId = session?.user?.id; - const { key } = req.body; - - const config = await getConfig(key); - if (sessionUserId !== config?.user_id) { - throw new Error("Unauthorized!"); - } - - return await removeConfig({ key }); - }, + POST: ConfigController.remove, }); export default route; diff --git a/app/pages/api/config-update.ts b/app/pages/api/config-update.ts index 9caac853a..69e779417 100644 --- a/app/pages/api/config-update.ts +++ b/app/pages/api/config-update.ts @@ -1,28 +1,9 @@ -import { getServerSession } from "next-auth"; - -import { updateConfig } from "@/db/config"; +import ConfigController from "@/server/config-controller"; import { api } from "../../server/nextkit"; -import { nextAuthOptions } from "./auth/[...nextauth]"; - const route = api({ - POST: async ({ req, res }) => { - const session = await getServerSession(req, res, nextAuthOptions); - const serverUserId = session?.user?.id; - const { key, user_id, data, published_state } = req.body; - - if (serverUserId !== user_id) { - throw new Error("Unauthorized!"); - } - - return await updateConfig({ - key, - data, - user_id, - published_state, - }); - }, + POST: ConfigController.update, }); export default route; diff --git a/app/server/config-controller.ts b/app/server/config-controller.ts new file mode 100644 index 000000000..ad0879f8f --- /dev/null +++ b/app/server/config-controller.ts @@ -0,0 +1,57 @@ +import { getServerSession } from "next-auth"; + +import { + createConfig, + getConfig, + removeConfig, + updateConfig, +} from "@/db/config"; +import { nextAuthOptions } from "@/pages/api/auth/[...nextauth]"; +import { controller } from "@/server/nextkit"; + +const ConfigController = controller({ + create: async ({ req, res }) => { + const session = await getServerSession(req, res, nextAuthOptions); + const userId = session?.user?.id; + const { data, published_state } = req.body; + + return await createConfig({ + key: data.key, + data, + userId, + published_state: published_state, + }); + }, + + remove: async ({ req, res }) => { + const session = await getServerSession(req, res, nextAuthOptions); + const sessionUserId = session?.user?.id; + const { key } = req.body; + + const config = await getConfig(key); + if (sessionUserId !== config?.user_id) { + throw new Error("Unauthorized!"); + } + + return await removeConfig({ key }); + }, + + update: async ({ req, res }) => { + const session = await getServerSession(req, res, nextAuthOptions); + const serverUserId = session?.user?.id; + const { key, user_id, data, published_state } = req.body; + + if (serverUserId !== user_id) { + throw new Error("Unauthorized!"); + } + + return await updateConfig({ + key, + data, + user_id, + published_state, + }); + }, +}); + +export default ConfigController; diff --git a/app/server/nextkit.ts b/app/server/nextkit.ts index a483c5eac..e7af7362a 100644 --- a/app/server/nextkit.ts +++ b/app/server/nextkit.ts @@ -1,4 +1,13 @@ -import createAPI from "nextkit"; +import createAPI, { NextkitHandler } from "nextkit"; + +/** Provides type hints */ +export const controller = < + THandlers extends Record> +>( + methods: THandlers +) => { + return methods; +}; export const api = createAPI({ async onError(_req, _res, error) {