Skip to content

Commit

Permalink
action to create a new artboard branch version and clone the current …
Browse files Browse the repository at this point in the history
…version designs and layers
  • Loading branch information
goodeats committed May 2, 2024
1 parent a51dfce commit f245c58
Show file tree
Hide file tree
Showing 32 changed files with 685 additions and 96 deletions.
1 change: 1 addition & 0 deletions app/components/layout/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export * from './card'
export * from './dashboard'
export * from './navbar'
export * from './page-footer'
export * from './page-header'
export * from './panel'
Expand Down
17 changes: 17 additions & 0 deletions app/components/layout/navbar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { createContainerComponent } from './utils'

// copied from v0.dev
// container for group of buttons
// - box-content so that border and padding is added on top of specified height and width
// specified height, width will depend on how many buttons
// gap-2 for separation between buttons
// rounded-md for rounded corners
// border and background color
const NavbarButtonGroup = createContainerComponent({
defaultTagName: 'div',
defaultClassName:
'box-content flex h-6 items-center gap-2 rounded-md border border-primary bg-primary-foreground p-1',
displayName: 'NavbarButtonGroup',
})

export { NavbarButtonGroup }
1 change: 0 additions & 1 deletion app/components/templates/combobox/nav-combobox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ export function ComboboxNav({
const [open, setOpen] = useState(false)
const params = useParams()
const paramsSlug = slugParam ? params[slugParam] : params.slug
console.log(params)
const currentEntity = entities.find(entity => entity.slug === paramsSlug)

return (
Expand Down
8 changes: 5 additions & 3 deletions app/components/templates/form/fetcher/icon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ export const FormFetcherIcon = ({
}: {
entityId?: IEntityId
type?: IEntityType
parentTypeId: entityParentIdTypeEnum
parentId: IEntityParentId
parentTypeId?: entityParentIdTypeEnum
parentId?: IEntityParentId
route: RoutePath
formId: string
schema: z.ZodSchema<any>
Expand Down Expand Up @@ -67,7 +67,9 @@ export const FormFetcherIcon = ({

<input type="hidden" name="no-js" value={String(!isHydrated)} />
{entityId && <input type="hidden" name="id" value={entityId} />}
<input type="hidden" name={parentTypeId} value={parentId} />
{parentId && parentTypeId && (
<input type="hidden" name={parentTypeId} value={parentId} />
)}
{type && <input type="hidden" name="type" value={type} />}

<PanelIconButton
Expand Down
4 changes: 3 additions & 1 deletion app/components/templates/form/fetcher/text.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,9 @@ export const FormFetcherText = ({

<input type="hidden" name="no-js" value={String(!isHydrated)} />
<input type="hidden" name="id" value={entityId} />
{parentId && <input type="hidden" name={parentTypeId} value={parentId} />}
{parentId && parentTypeId && (
<input type="hidden" name={parentTypeId} value={parentId} />
)}

<Input
className="flex h-8"
Expand Down
1 change: 1 addition & 0 deletions app/components/templates/navbar/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './nav-actions'
88 changes: 88 additions & 0 deletions app/components/templates/navbar/nav-actions.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import { type PopoverProps } from '@radix-ui/react-popover'
import { Link, useParams } from '@remix-run/react'
import { useState } from 'react'
import { Button } from '#app/components/ui/button'
import {
Command,
CommandEmpty,
CommandGroup,
CommandInput,
CommandItem,
} from '#app/components/ui/command'
import { Icon } from '#app/components/ui/icon'
import {
Popover,
PopoverContent,
PopoverTrigger,
} from '#app/components/ui/popover'
import { type IEntityWithSlug } from '#app/schema/entity'
import { cn } from '#app/utils/misc'
import { capitalize } from '#app/utils/string-formatting'

interface EntitySelectorProps extends PopoverProps {
entities: IEntityWithSlug[]
entitySingular?: string
entityPlural?: string
placeholder?: string
slugParam?: string
baseUrl: string
}

export function ComboboxNav({
entities,
entitySingular = 'entity',
entityPlural = 'entities',
placeholder = 'Select...',
slugParam,
baseUrl,
...props
}: EntitySelectorProps) {
const [open, setOpen] = useState(false)
const params = useParams()
const paramsSlug = slugParam ? params[slugParam] : params.slug
const currentEntity = entities.find(entity => entity.slug === paramsSlug)

return (
<Popover open={open} onOpenChange={setOpen} {...props}>
<PopoverTrigger asChild>
<Button
variant="outline"
role="combobox"
aria-label={placeholder}
aria-expanded={open}
className="flex-1 justify-between md:max-w-[200px] lg:max-w-[300px]"
>
{currentEntity?.name || placeholder}
<Icon
name="caret-sort"
className="ml-2 h-4 w-4 shrink-0 opacity-50"
/>
</Button>
</PopoverTrigger>
<PopoverContent className="w-[300px] p-0">
<Command>
<CommandInput placeholder={`Search ${entityPlural}...`} />
<CommandEmpty>No {entityPlural} found.</CommandEmpty>
<CommandGroup heading={capitalize(entityPlural)}>
{entities.map(entity => (
<CommandItem key={entity.id} onSelect={() => setOpen(false)}>
<Link prefetch="intent" to={`${baseUrl}/${entity.slug}`}>
{entity.name}
</Link>
<Icon
name="check"
className={cn(
'ml-auto h-4 w-4',
currentEntity?.id === entity.id
? 'opacity-100'
: 'opacity-0',
)}
/>
</CommandItem>
))}
</CommandGroup>
</Command>
</PopoverContent>
</Popover>
)
}
1 change: 1 addition & 0 deletions app/components/ui/icons/name.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export type IconName =
| 'crosshair-1'
| 'crosshair-2'
| 'dimensions'
| 'disc'
| 'dots-horizontal'
| 'download'
| 'envelope-closed'
Expand Down
7 changes: 7 additions & 0 deletions app/components/ui/icons/sprite.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 0 additions & 6 deletions app/models/artboard-branch/artboard-branch.create.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,6 @@ export const createDefaultArtboardBranchWithVersion = async ({
default: true,
versions: {
create: {
name: 'latest',
artboard: {
connect: {
id: artboard.id,
},
},
owner: {
connect: {
id: ownerId,
Expand Down
18 changes: 16 additions & 2 deletions app/models/artboard-branch/artboard-branch.get.server.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
import { z } from 'zod'
import { prisma } from '#app/utils/db.server'
import { type IArtboardBranchWithVersions } from './artboard-branch.server'
import {
type IArtboardBranch,
type IArtboardBranchWithVersions,
} from './artboard-branch.server'

export type queryArtboardBranchWhereArgsType = z.infer<typeof whereArgs>
const whereArgs = z.object({
id: z.string().optional(),
ownerId: z.string().optional(),
slug: z.string().optional(),
artboardId: z.string().optional(),
})

// TODO: Add schemas for each type of query and parse with zod
Expand All @@ -22,6 +26,16 @@ const validateQueryWhereArgsPresent = (
}
}

export const getArtboardBranch = async ({
where,
}: {
where: queryArtboardBranchWhereArgsType
}): Promise<IArtboardBranch | null> => {
validateQueryWhereArgsPresent(where)
const branch = await prisma.artboardBranch.findFirst({ where })
return branch
}

export const getArtboardBranchWithVersions = async ({
where,
}: {
Expand All @@ -33,7 +47,7 @@ export const getArtboardBranchWithVersions = async ({
include: {
versions: {
where: {
latest: true,
nextId: null,
},
take: 1,
},
Expand Down
65 changes: 65 additions & 0 deletions app/models/artboard-version/artboard-version.create.server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { type IntentActionArgs } from '#app/definitions/intent-action-args'
import { NewArtboardVersionSchema } from '#app/schema/artboard-version'
import { ValidateArtboardBranchParentSubmissionStrategy } from '#app/strategies/validate-submission.strategy'
import { validateEntitySubmission } from '#app/utils/conform-utils'
import { prisma } from '#app/utils/db.server'
import { type IArtboardBranch } from '../artboard-branch/artboard-branch.server'
import { type IUser } from '../user/user.server'
import { type IArtboardVersion } from './artboard-version.server'

export interface IArtboardVersionCreatedResponse {
success: boolean
message?: string
createdArtboardVersion?: IArtboardVersion
}

export const validateNewArtboardVersionSubmission = async ({
userId,
formData,
}: IntentActionArgs) => {
const strategy = new ValidateArtboardBranchParentSubmissionStrategy()

return await validateEntitySubmission({
userId,
formData,
schema: NewArtboardVersionSchema,
strategy,
})
}

export const createArtboardVersion = async ({
data,
}: {
data: {
ownerId: IUser['id']
branchId: IArtboardBranch['id']
name: string
slug: string
description: string
width?: number
height?: number
background?: string
}
}) => {
return await prisma.artboardVersion.create({
data,
})
}

/**
* Increment the version string by one.
* For example, "v0" becomes "v1", "v1" becomes "v2", etc.
* @param {string} name - The current version string.
* @returns {string} - The incremented version string.
*/
export const incrementVersionNameString = (name: string): string => {
// Assuming the prefix is always 'v'
const versionPrefix = name.slice(0, 1)
// Get the numeric part of the version
const versionNumber = parseInt(name.slice(1))
if (isNaN(versionNumber)) {
throw new Error(`Invalid version name string: ${name}`)
}
// Increment the version number and return the new version string
return `${versionPrefix}${versionNumber + 1}`
}
21 changes: 21 additions & 0 deletions app/models/artboard-version/artboard-version.delete.server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { prisma } from '#app/utils/db.server'
import { type IArtboardVersion } from './artboard-version.server'

export interface IArtboardVersionDeletedResponse {
success: boolean
message?: string
}

export const deleteArtboardVersions = ({
ids,
}: {
ids: IArtboardVersion['id'][]
}) => {
return prisma.artboardVersion.deleteMany({
where: {
id: {
in: ids,
},
},
})
}
17 changes: 16 additions & 1 deletion app/models/artboard-version/artboard-version.get.server.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { z } from 'zod'
import { zodStringOrNull } from '#app/schema/zod-helpers'
import { prisma } from '#app/utils/db.server'
import {
type IArtboardVersionWithDesignsAndLayers,
Expand All @@ -10,6 +11,9 @@ const whereArgs = z.object({
id: z.string().optional(),
ownerId: z.string().optional(),
slug: z.string().optional(),
branchId: z.string().optional(),
nextId: zodStringOrNull.optional(),
prevId: zodStringOrNull.optional(),
})

const includeDesigns = {
Expand Down Expand Up @@ -43,7 +47,7 @@ const includeDesignsAndLayers = {
const validateQueryWhereArgsPresent = (
where: queryArtboardVersionWhereArgsType,
) => {
const nullValuesAllowed: string[] = []
const nullValuesAllowed: string[] = ['nextId', 'prevId']
const missingValues: Record<string, any> = {}
for (const [key, value] of Object.entries(where)) {
const valueIsNull = value === null || value === undefined
Expand All @@ -61,6 +65,17 @@ const validateQueryWhereArgsPresent = (
}
}

export const getArtboardVersions = async ({
where,
}: {
where: queryArtboardVersionWhereArgsType
}): Promise<IArtboardVersion[]> => {
validateQueryWhereArgsPresent(where)
return await prisma.artboardVersion.findMany({
where,
})
}

export const getArtboardVersion = async ({
where,
}: {
Expand Down
19 changes: 19 additions & 0 deletions app/models/artboard-version/artboard-version.update.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
} from '#app/schema/artboard-version'
import { ValidateArtboardVersionSubmissionStrategy } from '#app/strategies/validate-submission.strategy'
import { validateEntitySubmission } from '#app/utils/conform-utils'
import { prisma } from '#app/utils/db.server'
import { findFirstArtboardVersionInstance } from '#app/utils/prisma-extensions-artboard-version'
import { type IArtboardVersion } from './artboard-version.server'

Expand Down Expand Up @@ -138,3 +139,21 @@ export const updateArtboardVersionBackground = async ({
return { success: false }
}
}

export const connectPrevAndNext = ({
prevId,
nextId,
}: {
prevId: IArtboardVersion['id']
nextId: IArtboardVersion['id']
}) => {
const connectNextToPrev = prisma.artboardVersion.update({
where: { id: prevId },
data: { nextId },
})
const connectPrevToNext = prisma.artboardVersion.update({
where: { id: nextId },
data: { prevId },
})
return [connectNextToPrev, connectPrevToNext]
}
Loading

0 comments on commit f245c58

Please sign in to comment.