diff --git a/frontend/graphql/createProject.graphql b/frontend/graphql/createProject.graphql new file mode 100644 index 0000000..ce694db --- /dev/null +++ b/frontend/graphql/createProject.graphql @@ -0,0 +1,7 @@ +mutation CreateProject($input: createProjectInput) { + createProject(input: $input) { + project { + id + } + } +} diff --git a/frontend/graphql/generated.ts b/frontend/graphql/generated.ts index dcd8671..bb19fe0 100644 --- a/frontend/graphql/generated.ts +++ b/frontend/graphql/generated.ts @@ -2056,6 +2056,22 @@ export type CreatePageMutationVariables = Exact<{ export type CreatePageMutation = { __typename?: 'Mutation', createPage?: Maybe<{ __typename?: 'createPagePayload', page?: Maybe<{ __typename?: 'Page', id: string }> }> }; +export type CreateProjectMutationVariables = Exact<{ + input?: Maybe; +}>; + + +export type CreateProjectMutation = ( + { __typename?: 'Mutation' } + & { createProject?: Maybe<( + { __typename?: 'createProjectPayload' } + & { project?: Maybe<( + { __typename?: 'Project' } + & Pick + )> } + )> } +); + export type GetGlobalQueryVariables = Exact<{ [key: string]: never; }>; @@ -2190,6 +2206,15 @@ export const CreatePage = ` } } `; +export const CreateProject = ` + mutation CreateProject($input: createProjectInput) { + createProject(input: $input) { + project { + id + } + } +} + `; export const GetGlobal = ` query GetGlobal { global { diff --git a/frontend/pages/projects/index.tsx b/frontend/pages/projects/index.tsx index 0f6ae6b..58677ee 100644 --- a/frontend/pages/projects/index.tsx +++ b/frontend/pages/projects/index.tsx @@ -12,6 +12,8 @@ import { NavBar, NavMenuMobile } from "@features/mainNavigation"; import { NAV_BLOCK } from "@features/mainNavigation/blocks"; import { NavBlockData } from "@features/mainNavigation/blocks/NavigationBlock"; import { + CreateProject, + CreateProjectInput, GetGlobal, GetGlobalQuery, GetGlobalQueryVariables, @@ -27,6 +29,7 @@ import { getPageCreatorPlugin, GlobalData, LocalizationsData, + ProjectDataCreateInput, } from "@plugins/usePagePlugin"; import { filterListNullableItems } from "@utils"; import { GetStaticProps } from "next"; @@ -34,7 +37,14 @@ import Link from "next/link"; import { useRouter } from "next/router"; import React from "react"; import { InlineBlocks, InlineForm } from "react-tinacms-inline"; -import { Form, FormOptions, useCMS, useForm, usePlugin } from "tinacms"; +import { + ContentCreatorPlugin, + Form, + FormOptions, + useCMS, + useForm, + usePlugin, +} from "tinacms"; interface Project { id: string; @@ -72,6 +82,14 @@ interface DynamicPageProps { }; } +export interface PreviewImageProps { + id: string; + type: string; + directory: string; + fileName: string; + previewSrc: string; +} + function wrap(value: T | T[]): T[] { if (Array.isArray(value)) { return value; @@ -100,13 +118,7 @@ export default function Index({ data }: DynamicPageProps) { availableLocales.splice(index, 1); return ( - + @@ -479,6 +491,12 @@ function useProjectPlugin(data: GlobalData): [GlobalData, Form] { }); usePlugin(creatorPlugin); + const projectCreatorPlugin = getProjectCreatorPlugin({ + locales: router.locales || [], + }); + + usePlugin(projectCreatorPlugin); + return [formData, form]; } @@ -575,3 +593,134 @@ function getProjectsData( ): Project[] | undefined { return projects as Project[]; } + +interface ProjectCreatorPluginOptions { + locales: string[]; +} + +export function getProjectCreatorPlugin( + options: ProjectCreatorPluginOptions +): ContentCreatorPlugin { + return { + __type: "content-creator", + name: "Add new project", + fields: [ + { + label: "Company name", + name: "companyName", + component: "text", + validate(name: string) { + if (!name) return "Required."; + }, + }, + { + label: "Path", + name: "path", + component: "text", + defaultValue: "/projects/", + description: "The path to the page ( e.g. /projects/something )", + validate(path: string) { + if (!path.startsWith("/projects/")) { + return "Path must start with /projects/"; + } + if (!path.charAt(10)) { + return "Path must contain something after /projects/"; + } + }, + }, + { + label: "Project type", + name: "projectType", + component: "text", + validate(type: string) { + if (!type) return "Required."; + }, + }, + { + label: "Description", + name: "description", + component: "textarea", + validate(description: string) { + if (!description) return "Required."; + }, + }, + { + label: "Locale", + name: "locale", + component: "select", + defaultValue: "en", + description: "Select a locale for this page", + // @ts-ignore + options: options.locales, + }, + { + label: "Link path", + name: "linkPath", + component: "text", + validate(linkPath: string) { + if (!linkPath) return "Required."; + if (!linkPath.startsWith("/")) { + return "Path should start with /"; + } + }, + }, + { + label: "Link label", + name: "linkLabel", + component: "text", + }, + { + label: "Image", + name: "image", + component: "image", + parse: (media) => media, + // @ts-ignore + uploadDir: () => "/", + previewSrc: (imageSrc: PreviewImageProps) => { + if (imageSrc.previewSrc === "") { + return "/images/default-image.png"; + } + return imageSrc.previewSrc; + }, + }, + ], + onSubmit: async (values, cms) => { + const input = getProjectCreateInput(values); + + try { + const response = await cms.api.strapi.fetchGraphql(CreateProject, { + input, + }); + + if (response.data) { + // @ts-ignore + cms.alerts.success("Changes saved!"); + window.location.href = `/${values.locale}${values.path}`; + } else { + // @ts-ignore + cms.alerts.error("Error while saving changes"); + } + } catch (error) { + console.log(error); + // @ts-ignore + cms.alerts.error("Error while saving changes"); + } + }, + }; +} + +export function getProjectCreateInput( + input: ProjectDataCreateInput +): CreateProjectInput { + return { + data: { + companyName: input.companyName, + projectType: input.projectType, + linkLabel: input.linkLabel || "Discover more", + linkPath: input.linkPath, + description: input.description, + path: input.path, + image: input.image.id, + }, + }; +} diff --git a/frontend/plugins/usePagePlugin.ts b/frontend/plugins/usePagePlugin.ts index 5cb3c24..e4f562a 100644 --- a/frontend/plugins/usePagePlugin.ts +++ b/frontend/plugins/usePagePlugin.ts @@ -3,6 +3,8 @@ import { SectionBlockData } from "@features/page"; import { CreatePage, CreatePageInput, + CreateProject, + CreateProjectInput, SaveChanges, UpdateGlobalInput, UpdateMenuInput, @@ -101,6 +103,10 @@ export interface Data { page: PageData; } +interface PreviewImageProps { + previewSrc: string; +} + export function usePagePlugin(data: Data): [Data, Form] { const cms = useCMS(); const router = useRouter(); @@ -179,7 +185,12 @@ export function usePagePlugin(data: Data): [Data, Form] { locales: router.locales || [], }); + const projectCreatorPlugin = getProjectCreatorPlugin({ + locales: router.locales || [], + }); + usePlugin(pageCreatorPlugin); + usePlugin(projectCreatorPlugin); return [formData, form]; } @@ -351,6 +362,132 @@ export function getPageCreatorPlugin( }; } +interface ProjectCreatorPluginOptions { + locales: string[]; +} + +export function getProjectCreatorPlugin( + options: ProjectCreatorPluginOptions +): ContentCreatorPlugin { + return { + __type: "content-creator", + name: "Add new project", + fields: [ + { + label: "Company name", + name: "companyName", + component: "text", + validate(name: string) { + if (!name) return "Required."; + }, + }, + { + label: "Path", + name: "path", + component: "text", + defaultValue: "/projects/", + description: "The path to the page ( e.g. /projects/something )", + validate(path: string) { + if (!path) { + return "Required."; + } + if (!path.startsWith("/projects")) { + return "Path must start with /projects"; + } + }, + }, + { + label: "Project type", + name: "projectType", + component: "text", + validate(type: string) { + if (!type) return "Required."; + }, + }, + { + label: "Description", + name: "description", + component: "textarea", + validate(description: string) { + if (!description) return "Required."; + }, + }, + { + label: "Locale", + name: "locale", + component: "select", + defaultValue: "en", + description: "Select a locale for this page", + // @ts-ignore + options: options.locales, + }, + { + label: "Link path", + name: "linkPath", + component: "text", + validate(linkPath: string) { + if (!linkPath) return "Required."; + if (!linkPath.startsWith("/")) { + return "Path should start with /"; + } + }, + }, + { + label: "Link label", + name: "linkLabel", + component: "text", + }, + { + label: "Image", + name: "image", + component: "image", + parse: (media) => media, + // @ts-ignore + uploadDir: () => "/", + previewSrc: (imageSrc: PreviewImageProps) => { + if (imageSrc.previewSrc === "") { + return "/images/default-image.png"; + } + return imageSrc.previewSrc; + }, + /*uploadDir={() => "/"} + previewSrc={(imageSrc) => { + if (imageSrc === "") { + return "/images/default-image.png"; + } + + return imageSrc; + }} + parse={(media) => { + return media as any; + }}*/ + }, + ], + onSubmit: async (values, cms) => { + const input = getProjectCreateInput(values); + + try { + const response = await cms.api.strapi.fetchGraphql(CreateProject, { + input, + }); + + if (response.data) { + // @ts-ignore + cms.alerts.success("Changes saved!"); + window.location.href = `/${values.locale}${values.path}`; + } else { + // @ts-ignore + cms.alerts.error("Error while saving changes"); + } + } catch (error) { + console.log(error); + // @ts-ignore + cms.alerts.error("Error while saving changes"); + } + }, + }; +} + export function getPageCreateInput( input: PageDataCreateInput ): CreatePageInput { @@ -363,6 +500,21 @@ export function getPageCreateInput( }; } +export function getProjectCreateInput( + input: ProjectDataCreateInput +): CreateProjectInput { + return { + data: { + companyName: input.companyName, + projectType: input.projectType, + linkLabel: input.linkLabel || "Discover more", + linkPath: input.linkPath, + description: input.description, + path: input.path, + }, + }; +} + function getGlobalSettingsInput(data: GlobalData): UpdateGlobalInput { return { data: {