Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support storing page ordering of folders in DB #850

Merged
merged 5 commits into from
Nov 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions apps/studio/prisma/generated/generatedEnums.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export const ResourceType = {
CollectionLink: "CollectionLink",
CollectionPage: "CollectionPage",
IndexPage: "IndexPage",
FolderMeta: "FolderMeta",
} as const
export type ResourceType = (typeof ResourceType)[keyof typeof ResourceType]
export const RoleType = {
Expand Down
2 changes: 2 additions & 0 deletions apps/studio/prisma/migrations/20241030080339_/migration.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
-- AlterEnum
ALTER TYPE "ResourceType" ADD VALUE 'FolderMeta';
1 change: 1 addition & 0 deletions apps/studio/prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ enum ResourceType {
CollectionLink // Can only ever be inside collection
CollectionPage // Can only live inside `Collection` resources
IndexPage // This denotes the index page of a folder or a collection
FolderMeta // This denotes some of the metadata of a folder (e.g. page ordering)
}

model User {
Expand Down
144 changes: 102 additions & 42 deletions apps/studio/public/assets/css/preview-tw.css
Original file line number Diff line number Diff line change
Expand Up @@ -1774,22 +1774,6 @@ video {
flex-basis: 0px;
}

.basis-\[calc\(\(100\%-2\.5rem\)\/2\)\] {
flex-basis: calc((100% - 2.5rem) / 2);
}

.basis-\[calc\(\(100\%-5rem\)\/3\)\] {
flex-basis: calc((100% - 5rem) / 3);
}

.basis-\[calc\(\(100\%-7\.5rem\)\/4\)\] {
flex-basis: calc((100% - 7.5rem) / 4);
}

.basis-full {
flex-basis: 100%;
}

.border-collapse {
border-collapse: collapse;
}
Expand Down Expand Up @@ -4120,6 +4104,18 @@ video {
position: static;
}

.focus\:left-0:where([data-rac])[data-focused] {
left: 0px;
}

.focus\:top-0:where([data-rac])[data-focused] {
top: 0px;
}

.focus\:z-50:where([data-rac])[data-focused] {
z-index: 50;
}

.focus\:h-auto:where([data-rac])[data-focused] {
height: auto;
}
Expand All @@ -4133,11 +4129,29 @@ video {
border-color: rgb(247 143 30 / var(--tw-border-opacity));
}

.focus\:bg-utility-highlight:where([data-rac])[data-focused] {
--tw-bg-opacity: 1;
background-color: rgb(251 191 36 / var(--tw-bg-opacity));
}

.focus\:text-base-content-strong:where([data-rac])[data-focused] {
--tw-text-opacity: 1;
color: rgb(17 24 39 / var(--tw-text-opacity));
}

.focus\:decoration-transparent:where([data-rac])[data-focused] {
text-decoration-color: transparent;
}

.focus\:outline-none:where([data-rac])[data-focused] {
outline: 2px solid transparent;
outline-offset: 2px;
}

.focus\:outline-0:where([data-rac])[data-focused] {
outline-width: 0px;
}

.focus\:ring-2:where([data-rac])[data-focused] {
--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0
var(--tw-ring-offset-width) var(--tw-ring-offset-color);
Expand Down Expand Up @@ -4170,10 +4184,38 @@ video {
--tw-ring-offset-width: 2px;
}

.focus\:transition-none:where([data-rac])[data-focused] {
transition-property: none;
}

.focus\:hover\:decoration-transparent:where([data-rac])[data-hovered]:where(
[data-rac]
)[data-focused] {
text-decoration-color: transparent;
}

.focus\:hover\:decoration-transparent:where(:not([data-rac])):hover:where(
[data-rac]
)[data-focused] {
text-decoration-color: transparent;
}

.focus\:static:where(:not([data-rac])):focus {
position: static;
}

.focus\:left-0:where(:not([data-rac])):focus {
left: 0px;
}

.focus\:top-0:where(:not([data-rac])):focus {
top: 0px;
}

.focus\:z-50:where(:not([data-rac])):focus {
z-index: 50;
}

.focus\:h-auto:where(:not([data-rac])):focus {
height: auto;
}
Expand All @@ -4187,11 +4229,29 @@ video {
border-color: rgb(247 143 30 / var(--tw-border-opacity));
}

.focus\:bg-utility-highlight:where(:not([data-rac])):focus {
--tw-bg-opacity: 1;
background-color: rgb(251 191 36 / var(--tw-bg-opacity));
}

.focus\:text-base-content-strong:where(:not([data-rac])):focus {
--tw-text-opacity: 1;
color: rgb(17 24 39 / var(--tw-text-opacity));
}

.focus\:decoration-transparent:where(:not([data-rac])):focus {
text-decoration-color: transparent;
}

.focus\:outline-none:where(:not([data-rac])):focus {
outline: 2px solid transparent;
outline-offset: 2px;
}

.focus\:outline-0:where(:not([data-rac])):focus {
outline-width: 0px;
}

.focus\:ring-2:where(:not([data-rac])):focus {
--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0
var(--tw-ring-offset-width) var(--tw-ring-offset-color);
Expand Down Expand Up @@ -4224,16 +4284,20 @@ video {
--tw-ring-offset-width: 2px;
}

.focus-visible\:left-0:where([data-rac])[data-focus-visible] {
left: 0px;
.focus\:transition-none:where(:not([data-rac])):focus {
transition-property: none;
}

.focus-visible\:top-0:where([data-rac])[data-focus-visible] {
top: 0px;
.focus\:hover\:decoration-transparent:where([data-rac])[data-hovered]:where(
:not([data-rac])
):focus {
text-decoration-color: transparent;
}

.focus-visible\:z-50:where([data-rac])[data-focus-visible] {
z-index: 50;
.focus\:hover\:decoration-transparent:where(:not([data-rac])):hover:where(
:not([data-rac])
):focus {
text-decoration-color: transparent;
}

.focus-visible\:-m-0:where([data-rac])[data-focus-visible] {
Expand Down Expand Up @@ -4306,18 +4370,6 @@ video {
text-decoration-color: transparent;
}

.focus-visible\:left-0:where(:not([data-rac])):focus-visible {
left: 0px;
}

.focus-visible\:top-0:where(:not([data-rac])):focus-visible {
top: 0px;
}

.focus-visible\:z-50:where(:not([data-rac])):focus-visible {
z-index: 50;
}

.focus-visible\:-m-0:where(:not([data-rac])):focus-visible {
margin: -0px;
}
Expand Down Expand Up @@ -4741,12 +4793,20 @@ video {
max-width: 760px;
}

.md\:max-w-\[calc\(\(100\%-2\.5rem\)\/2\)\] {
max-width: calc((100% - 2.5rem) / 2);
.md\:basis-\[calc\(\(100\%-2\.5rem\)\/2\)\] {
flex-basis: calc((100% - 2.5rem) / 2);
}

.md\:max-w-\[calc\(\(100\%-5rem\)\/3\)\] {
max-width: calc((100% - 5rem) / 3);
.md\:basis-\[calc\(\(100\%-5rem\)\/3\)\] {
flex-basis: calc((100% - 5rem) / 3);
}

.md\:basis-\[calc\(\(100\%-7\.5rem\)\/4\)\] {
flex-basis: calc((100% - 7.5rem) / 4);
}

.md\:basis-full {
flex-basis: 100%;
}

.md\:grid-cols-2 {
Expand Down Expand Up @@ -5008,12 +5068,12 @@ video {
gap: 2.5rem;
}

.lg\:gap-2 {
gap: 0.5rem;
.lg\:gap-12 {
gap: 3rem;
}

.lg\:gap-24 {
gap: 6rem;
.lg\:gap-2 {
gap: 0.5rem;
}

.lg\:gap-4 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,12 @@ const ERROR_COMPONENT_PROPS: Record<ResourceType, ErrorProps> = {
"To have access, ask your site admins to assign this folder to you",
buttonText: "Back to My Sites",
},
FolderMeta: {
title: "You don't have access to edit the page order of this folder.",
description:
"To have access, ask your site admins to assign this folder to you",
buttonText: "Back to My Sites",
},
} as const

interface PermissionsBoundaryProps {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,7 @@ function ComponentSelector() {
case ResourceType.IndexPage:
return []
case ResourceType.Folder:
case ResourceType.FolderMeta:
throw new Error(`Unsupported resource type: ${type}`)
default:
const exhaustiveCheck: never = type
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
import type { IconType } from "react-icons"
import { ResourceType } from "~prisma/generated/generatedEnums"
import { BiData, BiFile, BiFolder, BiHomeAlt, BiLink } from "react-icons/bi"
import {
BiData,
BiFile,
BiFolder,
BiHomeAlt,
BiLink,
BiSort,
} from "react-icons/bi"

export const ICON_MAPPINGS: Record<ResourceType, IconType> = {
[ResourceType.Page]: BiFile,
Expand All @@ -10,4 +17,5 @@ export const ICON_MAPPINGS: Record<ResourceType, IconType> = {
[ResourceType.CollectionLink]: BiLink,
[ResourceType.RootPage]: BiHomeAlt,
[ResourceType.IndexPage]: BiFile,
[ResourceType.FolderMeta]: BiSort,
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ export const useIsActive = (
return siteProps.folderId === currentResourceId
case ResourceType.CollectionLink:
return siteProps.linkId === currentResourceId
case ResourceType.FolderMeta:
// TODO: Not implemented yet
return false
default:
const _uncaught: never = type
throw new Error(`Unhandled case for useIsActive`)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,14 @@ import NextLink from "next/link"
import { HStack, Icon, Text, VStack } from "@chakra-ui/react"
import { Link } from "@opengovsg/design-system-react"
import { ResourceType } from "~prisma/generated/generatedEnums"
import { BiData, BiFile, BiFolder, BiHome, BiLink } from "react-icons/bi"
import {
BiData,
BiFile,
BiFolder,
BiHome,
BiLink,
BiSort,
} from "react-icons/bi"

import type { ResourceTableData } from "./types"
import { getLinkToResource } from "~/utils/resource"
Expand Down Expand Up @@ -40,6 +47,8 @@ export const TitleCell = ({
return BiFile
case ResourceType.CollectionLink:
return BiLink
case ResourceType.FolderMeta:
return BiSort
}
}, [type])

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,33 @@ describe("page.router", async () => {
expect(result).toMatchObject(expected)
})

it("should return the resource if resource type is FolderMeta and exists", async () => {
// Arrange
const { site, page, blob, navbar, footer } = await setupPageResource({
resourceType: "FolderMeta",
})
await setupAdminPermissions({
userId: session.userId ?? undefined,
siteId: site.id,
})
const expected = {
...pick(page, ["permalink", "title", "type"]),
navbar: omit(navbar, ["createdAt", "updatedAt"]),
footer: omit(footer, ["createdAt", "updatedAt"]),
content: blob.content,
}

// Act
const result = await caller.readPageAndBlob({
siteId: site.id,
pageId: Number(page.id),
})

// Assert
expect(result.type).toEqual("FolderMeta")
expect(result).toMatchObject(expected)
})

it("should return 404 if resource type is not a page", async () => {
// Arrange
const { site, folder } = await setupFolder()
Expand Down
3 changes: 2 additions & 1 deletion apps/studio/src/server/modules/page/page.router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,8 @@ export const pageRouter = router({
type !== ResourceType.Page &&
type !== ResourceType.CollectionPage &&
type !== ResourceType.RootPage &&
type !== ResourceType.IndexPage
type !== ResourceType.IndexPage &&
type !== ResourceType.FolderMeta
) {
throw new TRPCError({
code: "NOT_FOUND",
Expand Down
3 changes: 3 additions & 0 deletions apps/studio/src/server/modules/resource/resource.router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ export const resourceRouter = router({
.selectFrom("Resource")
.select(["title", "permalink", "type", "id"])
.where("Resource.type", "!=", ResourceType.RootPage)
.where("Resource.type", "!=", ResourceType.FolderMeta)
.where("Resource.siteId", "=", Number(siteId))
.$narrowType<{
type: Extract<
Expand Down Expand Up @@ -323,6 +324,7 @@ export const resourceRouter = router({
.selectFrom("Resource")
.where("Resource.siteId", "=", siteId)
.where("Resource.type", "!=", ResourceType.RootPage)
.where("Resource.type", "!=", ResourceType.FolderMeta)
.select((eb) => [eb.fn.countAll().as("totalCount")])

if (resourceId) {
Expand All @@ -342,6 +344,7 @@ export const resourceRouter = router({
.selectFrom("Resource")
.where("Resource.siteId", "=", siteId)
.where("Resource.type", "!=", ResourceType.RootPage)
.where("Resource.type", "!=", ResourceType.FolderMeta)
.orderBy("Resource.updatedAt", "desc")
.orderBy("Resource.title", "asc")
.offset(offset)
Expand Down
Loading
Loading