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

ISOM-1292: Add publish endpoint #381

Merged
merged 13 commits into from
Aug 12, 2024
32 changes: 16 additions & 16 deletions apps/studio/prisma/generated/generatedEnums.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
export const ResourceState = {
Draft: "Draft",
Published: "Published",
} as const
export type ResourceState = (typeof ResourceState)[keyof typeof ResourceState]
Draft: "Draft",
Published: "Published"
} as const;
export type ResourceState = (typeof ResourceState)[keyof typeof ResourceState];
export const ResourceType = {
RootPage: "RootPage",
Page: "Page",
Folder: "Folder",
Collection: "Collection",
CollectionPage: "CollectionPage",
} as const
export type ResourceType = (typeof ResourceType)[keyof typeof ResourceType]
RootPage: "RootPage",
Page: "Page",
Folder: "Folder",
Collection: "Collection",
CollectionPage: "CollectionPage"
} as const;
export type ResourceType = (typeof ResourceType)[keyof typeof ResourceType];
export const RoleType = {
Admin: "Admin",
Editor: "Editor",
Publisher: "Publisher",
} as const
export type RoleType = (typeof RoleType)[keyof typeof RoleType]
Admin: "Admin",
Editor: "Editor",
Publisher: "Publisher"
} as const;
export type RoleType = (typeof RoleType)[keyof typeof RoleType];
194 changes: 94 additions & 100 deletions apps/studio/prisma/generated/generatedTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,103 +6,97 @@ export type Timestamp = ColumnType<Date, Date | string, Date | string>;

import type { ResourceState, ResourceType, RoleType } from "./generatedEnums";

export type Generated<T> =
T extends ColumnType<infer S, infer I, infer U>
? ColumnType<S, I | undefined, U>
: ColumnType<T, T | undefined, T>
export type Timestamp = ColumnType<Date, Date | string, Date | string>

export interface Blob {
id: GeneratedAlways<string>
/**
* @kyselyType(PrismaJson.BlobJsonContent)
* [BlobJsonContent]
*/
content: PrismaJson.BlobJsonContent
}
export interface Footer {
id: GeneratedAlways<number>
siteId: number
/**
* @kyselyType(PrismaJson.FooterJsonContent)
* [FooterJsonContent]
*/
content: PrismaJson.FooterJsonContent
}
export interface Navbar {
id: GeneratedAlways<number>
siteId: number
/**
* @kyselyType(PrismaJson.NavbarJsonContent)
* [NavbarJsonContent]
*/
content: PrismaJson.NavbarJsonContent
}
export interface Permission {
id: GeneratedAlways<number>
resourceId: string
userId: string
role: RoleType
}
export interface Resource {
id: GeneratedAlways<string>
title: string
permalink: string
siteId: number
parentId: string | null
publishedVersionId: string | null
draftBlobId: string | null
state: Generated<ResourceState | null>
type: ResourceType
}
export interface Site {
id: GeneratedAlways<number>
name: string
/**
* @kyselyType(PrismaJson.SiteJsonConfig)
* [SiteJsonConfig]
*/
config: PrismaJson.SiteJsonConfig
/**
* @kyselyType(PrismaJson.SiteThemeJson)
* [SiteThemeJson]
*/
theme: PrismaJson.SiteThemeJson | null
}
export interface SiteMember {
userId: string
siteId: number
}
export interface User {
id: string
name: string
email: string
phone: string
preferredName: string | null
}
export interface VerificationToken {
identifier: string
token: string
attempts: Generated<number>
expires: Timestamp
}
export interface Version {
id: GeneratedAlways<string>
versionNum: number
resourceId: string
blobId: string
publishedAt: Generated<Timestamp>
publishedBy: string
}
export interface DB {
Blob: Blob
Footer: Footer
Navbar: Navbar
Permission: Permission
Resource: Resource
Site: Site
SiteMember: SiteMember
User: User
VerificationToken: VerificationToken
Version: Version
}
export type Blob = {
id: GeneratedAlways<string>;
/**
* @kyselyType(PrismaJson.BlobJsonContent)
* [BlobJsonContent]
*/
content: PrismaJson.BlobJsonContent;
};
export type Footer = {
id: GeneratedAlways<number>;
siteId: number;
/**
* @kyselyType(PrismaJson.FooterJsonContent)
* [FooterJsonContent]
*/
content: PrismaJson.FooterJsonContent;
};
export type Navbar = {
id: GeneratedAlways<number>;
siteId: number;
/**
* @kyselyType(PrismaJson.NavbarJsonContent)
* [NavbarJsonContent]
*/
content: PrismaJson.NavbarJsonContent;
};
export type Permission = {
id: GeneratedAlways<number>;
resourceId: string;
userId: string;
role: RoleType;
};
export type Resource = {
id: GeneratedAlways<string>;
title: string;
permalink: string;
siteId: number;
parentId: string | null;
publishedVersionId: string | null;
draftBlobId: string | null;
state: Generated<ResourceState | null>;
type: ResourceType;
};
export type Site = {
id: GeneratedAlways<number>;
name: string;
/**
* @kyselyType(PrismaJson.SiteJsonConfig)
* [SiteJsonConfig]
*/
config: PrismaJson.SiteJsonConfig;
/**
* @kyselyType(PrismaJson.SiteThemeJson)
* [SiteThemeJson]
*/
theme: PrismaJson.SiteThemeJson | null;
};
export type SiteMember = {
userId: string;
siteId: number;
};
export type User = {
id: string;
name: string;
email: string;
phone: string;
preferredName: string | null;
};
export type VerificationToken = {
identifier: string;
token: string;
attempts: Generated<number>;
expires: Timestamp;
};
export type Version = {
id: GeneratedAlways<string>;
versionNum: number;
resourceId: string;
blobId: string;
publishedAt: Generated<Timestamp>;
publishedBy: string;
};
export type DB = {
Blob: Blob;
Footer: Footer;
Navbar: Navbar;
Permission: Permission;
Resource: Resource;
Site: Site;
SiteMember: SiteMember;
User: User;
VerificationToken: VerificationToken;
Version: Version;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { Skeleton } from "@chakra-ui/react"
import { Button, useToast } from "@opengovsg/design-system-react"

import { withSuspense } from "~/hocs/withSuspense"
import { trpc } from "~/utils/trpc"

interface PublishButtonProps {
pageId: number
siteId: number
}

const SuspendablePublishButton = ({
pageId,
siteId,
}: PublishButtonProps): JSX.Element => {
const toast = useToast()
const utils = trpc.useUtils()

const [currPage] = trpc.page.readPage.useSuspenseQuery({ pageId, siteId })

const publishFailureMsg =
"Failed to publish page. Please contact Isomer support."
const publishSuccessMsg = "Page published successfully"

const { mutate, isLoading } = trpc.page.publishPage.useMutation({
onSuccess: async () => {
toast({
status: "success",
title: publishSuccessMsg,
})
await utils.page.readPage.invalidate({ pageId, siteId })
},
onError: async (error) => {
console.error(`Error occurred when publishing page: ${error.message}`)
toast({
status: "error",
title: publishFailureMsg,
})
await utils.page.readPage.invalidate({ pageId, siteId })
},
})

const handlePublish = () => {
const coercedSiteId = Number(siteId)
const coercedPageId = Number(pageId)
if (coercedSiteId && coercedPageId)
mutate({ pageId: coercedPageId, siteId: coercedSiteId })
}
return (
<Button
variant="solid"
size="sm"
onClick={handlePublish}
isLoading={isLoading}
isDisabled={!currPage.draftBlobId}
>
Publish
</Button>
)
}

const PublishButton = withSuspense(
SuspendablePublishButton,
<Skeleton width={"100%"} height={"100%"} />,
)
export default PublishButton
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@ import { Breadcrumb } from "@opengovsg/design-system-react"
import { ADMIN_NAVBAR_HEIGHT } from "~/constants/layouts"
import { useQueryParse } from "~/hooks/useQueryParse"
import { editPageSchema } from "../schema"
import PublishButton from "./PublishButton"

export const SiteEditNavbar = (): JSX.Element => {
const { siteId } = useQueryParse(editPageSchema)
const { siteId, pageId } = useQueryParse(editPageSchema)

return (
<Flex flex="0 0 auto" gridColumn="1/-1">
<Flex
Expand Down Expand Up @@ -36,6 +38,12 @@ export const SiteEditNavbar = (): JSX.Element => {
<BreadcrumbLink href="#">Current page</BreadcrumbLink>
</BreadcrumbItem>
</Breadcrumb>

{pageId && siteId && (
<Flex justifyContent={"end"} alignItems={"center"}>
<PublishButton pageId={pageId} siteId={siteId} />
</Flex>
)}
</Flex>
</Flex>
)
Expand Down
11 changes: 11 additions & 0 deletions apps/studio/src/schemas/page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ export const getEditPageSchema = z.object({
siteId: z.number().min(1),
})

export const getPageSchema = z.object({
pageId: z.number().min(1),
siteId: z.number().min(1),
})

export const reorderBlobSchema = z.object({
pageId: z.number().min(1),
from: z.number().min(0),
Expand Down Expand Up @@ -67,3 +72,9 @@ export const createPageSchema = z.object({
// NOTE: implies that top level pages are allowed
folderId: z.number().min(1).optional(),
})

// TODO: siteId should be taken from user's context (not input)
export const publishPageSchema = z.object({
pageId: z.number().min(1),
siteId: z.number().min(1),
})
3 changes: 2 additions & 1 deletion apps/studio/src/server/modules/database/database.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { type DB } from "~prisma/generated/generatedTypes"
import { Kysely, PostgresDialect } from "kysely"
import { PostgresDialect } from "kysely"
import pg from "pg"

import { env } from "~/env.mjs"
import { Kysely } from "./types"

const connectionString = `${env.DATABASE_URL}`

Expand Down
Loading
Loading