From ad1b07e681ad8bed825e5a77acb09dd835ed0f2f Mon Sep 17 00:00:00 2001 From: Pat Needham Date: Mon, 22 Apr 2024 17:44:34 -0400 Subject: [PATCH] panel entity update design type template through fetcher template and api --- .../design-type.update.template.server.ts | 81 ++++++++++++++ .../design.type.template.update.style.tsx | 53 +++++++++ ...idebars.panel.artboard-version.designs.tsx | 38 +++---- app/schema/template.ts | 15 ++- .../design-type/update-template.service.ts | 99 +++++++++++++++++ ...date-entity-values.design.type.template.ts | 104 ++++++++++++++++++ app/utils/design.ts | 15 ++- app/utils/routes.utils.ts | 17 +++ 8 files changed, 393 insertions(+), 29 deletions(-) create mode 100644 app/models/design-type/design-type.update.template.server.ts create mode 100644 app/routes/resources+/api.v1+/design.type.template.update.style.tsx create mode 100644 app/services/design-type/update-template.service.ts create mode 100644 app/strategies/component/dashboard-panel/update-entity/update-entity-values.design.type.template.ts diff --git a/app/models/design-type/design-type.update.template.server.ts b/app/models/design-type/design-type.update.template.server.ts new file mode 100644 index 00000000..91bbd51a --- /dev/null +++ b/app/models/design-type/design-type.update.template.server.ts @@ -0,0 +1,81 @@ +import { type IntentActionArgs } from '#app/definitions/intent-action-args' +import { + EditDesignTemplateStyleSchema, + type DesignTemplateUpdateSchemaType, +} from '#app/schema/template' +import { ValidateDesignParentSubmissionStrategy } from '#app/strategies/validate-submission.strategy' +import { validateEntitySubmission } from '#app/utils/conform-utils' +import { findFirstTemplateInstance } from '#app/utils/prisma-extensions-template' +import { type IDesign } from '../design.server' +import { type ITemplate } from '../template.server' + +export interface IDesignTypeTemplateUpdatedResponse { + success: boolean + message?: string + updatedTemplate?: ITemplate +} + +const validateUpdateSubmission = async ({ + userId, + formData, + schema, +}: IntentActionArgs & { + schema: DesignTemplateUpdateSchemaType +}) => { + const strategy = new ValidateDesignParentSubmissionStrategy() + + return await validateEntitySubmission({ + userId, + formData, + schema, + strategy, + }) +} + +export async function validateDesignTypeUpdateTemplateStyleSubmission( + args: IntentActionArgs, +) { + return validateUpdateSubmission({ + ...args, + schema: EditDesignTemplateStyleSchema, + }) +} + +const getTemplateInstance = async ({ id }: { id: ITemplate['id'] }) => { + return await findFirstTemplateInstance({ + where: { id }, + }) +} + +// updating instance instead of regular prism update +// this may not be easier, but it's more explicit +export const updateDesignTypeTemplateStyle = async ({ + id, + designId, + style, +}: { + id: ITemplate['id'] + designId: IDesign['id'] + style: string +}): Promise => { + const template = await getTemplateInstance({ id }) + if (!template) return { success: false } + + try { + const data = EditDesignTemplateStyleSchema.parse({ id, designId, style }) + template.style = data.style + template.updatedAt = new Date() + await template.save() + + return { success: true, updatedTemplate: template } + } catch (error) { + // consider how to handle this error where this is called + console.log('updateDesignTypeTemplateStyle error:', error) + const errorType = error instanceof Error + const errorMessage = errorType ? error.message : 'An unknown error occurred' + return { + success: false, + message: errorMessage, + } + } +} diff --git a/app/routes/resources+/api.v1+/design.type.template.update.style.tsx b/app/routes/resources+/api.v1+/design.type.template.update.style.tsx new file mode 100644 index 00000000..2ad5ffd1 --- /dev/null +++ b/app/routes/resources+/api.v1+/design.type.template.update.style.tsx @@ -0,0 +1,53 @@ +import { + type LoaderFunctionArgs, + json, + type DataFunctionArgs, +} from '@remix-run/node' +import { redirectBack } from 'remix-utils/redirect-back' +import { + updateDesignTypeTemplateStyle, + validateDesignTypeUpdateTemplateStyleSubmission, +} from '#app/models/design-type/design-type.update.template.server' +import { validateNoJS } from '#app/schema/form-data' +import { requireUserId } from '#app/utils/auth.server' + +// https://www.epicweb.dev/full-stack-components + +export async function loader({ request }: LoaderFunctionArgs) { + await requireUserId(request) + return json({}) +} + +export async function action({ request }: DataFunctionArgs) { + const userId = await requireUserId(request) + const formData = await request.formData() + const noJS = validateNoJS({ formData }) + + let updateSuccess = false + const { status, submission } = + await validateDesignTypeUpdateTemplateStyleSubmission({ + userId, + formData, + }) + + if (status === 'success') { + const { success } = await updateDesignTypeTemplateStyle({ + userId, + ...submission.value, + }) + updateSuccess = success + } + + if (noJS) { + throw redirectBack(request, { + fallback: '/', + }) + } + + return json( + { status, submission }, + { + status: status === 'error' || !updateSuccess ? 404 : 200, + }, + ) +} diff --git a/app/routes/sketch+/projects+/$projectSlug_+/artboards+/$artboardSlug+/__components/sidebars.panel.artboard-version.designs.tsx b/app/routes/sketch+/projects+/$projectSlug_+/artboards+/$artboardSlug+/__components/sidebars.panel.artboard-version.designs.tsx index 4f54d2aa..af97d7e9 100644 --- a/app/routes/sketch+/projects+/$projectSlug_+/artboards+/$artboardSlug+/__components/sidebars.panel.artboard-version.designs.tsx +++ b/app/routes/sketch+/projects+/$projectSlug_+/artboards+/$artboardSlug+/__components/sidebars.panel.artboard-version.designs.tsx @@ -21,9 +21,6 @@ export const PanelArtboardVersionDesigns = ({ const designTypePanels = designsByTypeToPanelArray({ designs: orderedDesigns, }) - // remove trim after testing actions work for one design type - const designsTrimmed = designTypePanels.slice(0, 7) - // const designsTrimmed = designTypePanels const strategyEntityNew = new DashboardPanelCreateArtboardVersionDesignTypeStrategy() @@ -36,24 +33,23 @@ export const PanelArtboardVersionDesigns = ({ return (
- {designsTrimmed.length > 0 && - designsTrimmed.map((designPanel, index) => { - const { type, designs, strategyEntityValues } = designPanel - return ( - - ) - })} + {designTypePanels.map((designPanel, index) => { + const { type, designs, strategyEntityValues } = designPanel + return ( + + ) + })}
) } diff --git a/app/schema/template.ts b/app/schema/template.ts index 95109efb..3b201e20 100644 --- a/app/schema/template.ts +++ b/app/schema/template.ts @@ -1,12 +1,23 @@ import { z } from 'zod' +import { type ObjectValues } from '#app/utils/typescript-helpers' + +export const TemplateStyleTypeEnum = { + TRIANGLE: 'triangle', // my only shape so far + // add more styles here +} as const +export type templateStyleTypeEnum = ObjectValues +const TemplateStyleSchema = z.nativeEnum(TemplateStyleTypeEnum) export const TemplateDataSchema = z.object({ designId: z.string(), - style: z.enum(['triangle']).optional(), + style: TemplateStyleSchema.optional(), }) +export type DesignTemplateUpdateSchemaType = + typeof EditDesignTemplateStyleSchema + export const EditDesignTemplateStyleSchema = z.object({ id: z.string(), designId: z.string(), - style: z.enum(['triangle']), + style: TemplateStyleSchema, }) diff --git a/app/services/design-type/update-template.service.ts b/app/services/design-type/update-template.service.ts new file mode 100644 index 00000000..70ae00af --- /dev/null +++ b/app/services/design-type/update-template.service.ts @@ -0,0 +1,99 @@ +import { type User } from '@sentry/remix' +import { + type IDesignTypeFillUpdatedResponse, + updateDesignTypeFillBasis, + updateDesignTypeFillValue, + updateDesignTypeFillStyle, +} from '#app/models/design-type/design-type.update.fill.server' +import { type IDesign } from '#app/models/design.server' +import { type IFill } from '#app/models/fill.server' + +export const updateDesignTypeFillValueService = async ({ + userId, + id, + designId, + value, +}: { + userId: User['id'] + id: IFill['id'] + designId: IDesign['id'] + value: number +}): Promise => { + try { + return await updateDesignTypeFillValue({ + id, + designId, + value, + }) + // later will be adding Activity class + // i.e, edit history so you can undo changes and/or see who made them + } catch (error) { + console.log('updateDesignTypeFillValueService error:', error) + const errorType = error instanceof Error + const errorMessage = errorType ? error.message : 'An unknown error occurred' + return { + success: false, + message: errorMessage, + } + } +} + +export const updateDesignTypeFillBasisService = async ({ + userId, + id, + designId, + basis, +}: { + userId: User['id'] + id: IFill['id'] + designId: IDesign['id'] + basis: string +}): Promise => { + try { + return await updateDesignTypeFillBasis({ + id, + designId, + basis, + }) + // later will be adding Activity class + // i.e, edit history so you can undo changes and/or see who made them + } catch (error) { + console.log('updateDesignTypeFillBasisService error:', error) + const errorType = error instanceof Error + const errorMessage = errorType ? error.message : 'An unknown error occurred' + return { + success: false, + message: errorMessage, + } + } +} + +export const updateDesignTypeFillStyleService = async ({ + userId, + id, + designId, + style, +}: { + userId: User['id'] + id: IFill['id'] + designId: IDesign['id'] + style: string +}): Promise => { + try { + return await updateDesignTypeFillStyle({ + id, + designId, + style, + }) + // later will be adding Activity class + // i.e, edit history so you can undo changes and/or see who made them + } catch (error) { + console.log('updateDesignTypeFillStyleService error:', error) + const errorType = error instanceof Error + const errorMessage = errorType ? error.message : 'An unknown error occurred' + return { + success: false, + message: errorMessage, + } + } +} diff --git a/app/strategies/component/dashboard-panel/update-entity/update-entity-values.design.type.template.ts b/app/strategies/component/dashboard-panel/update-entity/update-entity-values.design.type.template.ts new file mode 100644 index 00000000..8f0819b0 --- /dev/null +++ b/app/strategies/component/dashboard-panel/update-entity/update-entity-values.design.type.template.ts @@ -0,0 +1,104 @@ +import { type IDesignWithTemplate } from '#app/models/design.server' +import { EntityFormType, EntityParentIdType } from '#app/schema/entity' +import { + EditDesignTemplateStyleSchema, + TemplateStyleTypeEnum, +} from '#app/schema/template' +import { Routes } from '#app/utils/routes.utils' +import { transformEntityEnumValueForSelect } from '#app/utils/string-formatting' +import { + type IPanelEntityFormArgsOptionalMultiple, + type IDashboardPanelUpdateEntityValuesStrategy, + type IPanelEntityFormArgs, +} from './update-entity-values' + +const baseRoute = Routes.RESOURCES.API.V1.DESIGN.TYPE.TEMPLATE.UPDATE +const globalEntityFormArgs = { + parentTypeId: EntityParentIdType.DESIGN_ID, +} +const globalTemplateStyleArgs = { + route: baseRoute.STYLE, + formType: EntityFormType.SELECT, + formId: 'design-type-update-template-style', + schema: EditDesignTemplateStyleSchema, + label: 'Style', +} + +export class DashboardPanelUpdateDesignTypeTemplateValuesStrategy + implements IDashboardPanelUpdateEntityValuesStrategy +{ + getMainPanelForm({ + entity, + }: { + entity: IDesignWithTemplate + }): IPanelEntityFormArgsOptionalMultiple { + const { template } = entity + const { style } = template + + const sharedEntityFormArgs = { + ...globalEntityFormArgs, + entityId: template.id, + parentId: entity.id, + } + + const optionsStyle = Object.values(TemplateStyleTypeEnum).map( + templateStyleEnum => ({ + [templateStyleEnum]: + transformEntityEnumValueForSelect(templateStyleEnum), + }), + ) + const templateStyleArgs = { + ...sharedEntityFormArgs, + ...globalTemplateStyleArgs, + options: optionsStyle, + defaultValue: { style }, + } + return templateStyleArgs + } + + getPopoverForms({ + entity, + }: { + entity: IDesignWithTemplate + }): IPanelEntityFormArgs[] { + const { template } = entity + const { style } = template + const sharedEntityFormArgs = { + ...globalEntityFormArgs, + entityId: template.id, + parentId: template.id, + } + + const optionsStyle = Object.values(TemplateStyleTypeEnum).map( + templateStyleEnum => ({ + [templateStyleEnum]: + transformEntityEnumValueForSelect(templateStyleEnum), + }), + ) + + const templateStyleArgs = { + ...sharedEntityFormArgs, + ...globalTemplateStyleArgs, + options: optionsStyle, + defaultValue: { style }, + } + + return [templateStyleArgs] + } + + getPopoverTriggerColor({ + entity, + }: { + entity: IDesignWithTemplate + }): undefined { + return undefined + } + + getPanelFormatIcon({ entity }: { entity: IDesignWithTemplate }): undefined { + return undefined + } + + getPanelBasisIcon({ entity }: { entity: IDesignWithTemplate }): undefined { + return undefined + } +} diff --git a/app/utils/design.ts b/app/utils/design.ts index 90ec08ce..c7af7c2d 100644 --- a/app/utils/design.ts +++ b/app/utils/design.ts @@ -30,6 +30,7 @@ import { DashboardPanelUpdateDesignTypePaletteValuesStrategy } from '#app/strate import { DashboardPanelUpdateDesignTypeRotateValuesStrategy } from '#app/strategies/component/dashboard-panel/update-entity/update-entity-values.design.type.rotate' import { DashboardPanelUpdateDesignTypeSizeValuesStrategy } from '#app/strategies/component/dashboard-panel/update-entity/update-entity-values.design.type.size' import { DashboardPanelUpdateDesignTypeStrokeValuesStrategy } from '#app/strategies/component/dashboard-panel/update-entity/update-entity-values.design.type.stroke' +import { DashboardPanelUpdateDesignTypeTemplateValuesStrategy } from '#app/strategies/component/dashboard-panel/update-entity/update-entity-values.design.type.template' import { orderLinkedItems } from './linked-list.utils' import { safelyAssignValue } from './typescript-helpers' @@ -394,7 +395,7 @@ export const designsByTypeToPanelArray = ({ designLines, designRotates, designLayouts, - // designTemplates, + designTemplates, } = designs const strategyLayoutEntityValues = @@ -411,6 +412,8 @@ export const designsByTypeToPanelArray = ({ new DashboardPanelUpdateDesignTypeLineValuesStrategy() const strategyRotateEntityValues = new DashboardPanelUpdateDesignTypeRotateValuesStrategy() + const strategyTemplateEntityValues = + new DashboardPanelUpdateDesignTypeTemplateValuesStrategy() return [ { @@ -448,11 +451,11 @@ export const designsByTypeToPanelArray = ({ designs: designRotates, strategyEntityValues: strategyRotateEntityValues, }, - // { - // type: DesignTypeEnum.TEMPLATE, - // designs: designTemplates, - // strategyEntityValues, - // }, + { + type: DesignTypeEnum.TEMPLATE, + designs: designTemplates, + strategyEntityValues: strategyTemplateEntityValues, + }, ] } diff --git a/app/utils/routes.utils.ts b/app/utils/routes.utils.ts index af13e9e0..30ff7b28 100644 --- a/app/utils/routes.utils.ts +++ b/app/utils/routes.utils.ts @@ -103,6 +103,10 @@ import { loader as apiV1DesignTypeStrokeValueLoader, action as apiV1DesignTypeStrokeValueAction, } from '#app/routes/resources+/api.v1+/design.type.stroke.update.value' +import { + loader as apiV1DesignTypeTemplateStyleLoader, + action as apiV1DesignTypeTemplateStyleAction, +} from '#app/routes/resources+/api.v1+/design.type.template.update.style' import { type ExtractStringValues } from './typescript-helpers' export type RoutePath = ExtractStringValues @@ -176,6 +180,11 @@ export const Routes = { VALUE: `${pathBase}/design/type/rotate/update/value`, }, }, + TEMPLATE: { + UPDATE: { + STYLE: `${pathBase}/design/type/template/update/style`, + }, + }, }, }, }, @@ -236,6 +245,8 @@ export interface ApiRouteLoaders { .VALUE]: typeof apiV1DesignTypeRotateValueLoader [Routes.RESOURCES.API.V1.DESIGN.TYPE.ROTATE.UPDATE .BASIS]: typeof apiV1DesignTypeRotateBasisLoader + [Routes.RESOURCES.API.V1.DESIGN.TYPE.TEMPLATE.UPDATE + .STYLE]: typeof apiV1DesignTypeTemplateStyleLoader } export const loaders: ApiRouteLoaders = { @@ -291,6 +302,8 @@ export const loaders: ApiRouteLoaders = { apiV1DesignTypeRotateValueLoader, [Routes.RESOURCES.API.V1.DESIGN.TYPE.ROTATE.UPDATE.BASIS]: apiV1DesignTypeRotateBasisLoader, + [Routes.RESOURCES.API.V1.DESIGN.TYPE.TEMPLATE.UPDATE.STYLE]: + apiV1DesignTypeTemplateStyleLoader, } export function getLoaderType( @@ -354,6 +367,8 @@ export interface ApiRouteActions { .VALUE]: typeof apiV1DesignTypeRotateValueAction [Routes.RESOURCES.API.V1.DESIGN.TYPE.ROTATE.UPDATE .BASIS]: typeof apiV1DesignTypeRotateBasisAction + [Routes.RESOURCES.API.V1.DESIGN.TYPE.TEMPLATE.UPDATE + .STYLE]: typeof apiV1DesignTypeTemplateStyleAction } export const actions: ApiRouteActions = { @@ -409,6 +424,8 @@ export const actions: ApiRouteActions = { apiV1DesignTypeRotateValueAction, [Routes.RESOURCES.API.V1.DESIGN.TYPE.ROTATE.UPDATE.BASIS]: apiV1DesignTypeRotateBasisAction, + [Routes.RESOURCES.API.V1.DESIGN.TYPE.TEMPLATE.UPDATE.STYLE]: + apiV1DesignTypeTemplateStyleAction, } export function getActionType(