-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge remote-tracking branch 'origin/main' into feat/layout-cls
- Loading branch information
Showing
78 changed files
with
1,989 additions
and
552 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
export const ISOMER_ADMINS = [ | ||
"alex", | ||
"jan", | ||
"jiachin", | ||
"sehyun", | ||
"harish", | ||
"zhongjun", | ||
"adriangoh", | ||
"shazli", | ||
"jinhui", | ||
] | ||
|
||
export const ISOMER_MIGRATORS = [ | ||
"tingshian", | ||
"hakeem", | ||
"elora", | ||
"junxiang", | ||
"rayyan", | ||
"yongteng", | ||
"huaying", | ||
"weiping", | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
# Using these scripts | ||
|
||
1. first, run `npm run jump` for production or `npm run jump:<env>` | ||
a. if you are running this against production, find another engineer to pair | ||
|
||
2. then, update your `$DATABASE_URL` in your local `apps/studio/.env` to the one for the environment | ||
b. ensure that the port you have specified is 5433 as we are using a jump host to tunnel our connection | ||
|
||
3. update the script you want to invoke to pass in the correct arguments | ||
4. invoke the command via `source .env && npx tsx <path_to_script>` | ||
5. note that tsx might export the env vars to be exported. Hence, minor change to the `.env` is required to add `export` before each required env var. | ||
|
||
## Create site script | ||
|
||
1. On the last line, update the site name to the name you require. This should be exactly as what should be shown on Studio (e.g. "Site ABC") | ||
2. This already adds the Isomer admins and migrators with the Admin role. | ||
|
||
## Add users to site | ||
|
||
1. Edit the array of `User` objects to be added at the end of the script | ||
2. Each entry contains 4 fields: email, name, phone and role. Note that name, phone and role are optional fields. If left empty, name will be empty string, phone will be empty string and role will default to Editor. | ||
|
||
## Update all user permissions for site | ||
|
||
1. This script updates all users of a site to have a specific `RoleType` | ||
2. Edit the `siteId` and `role` at the end of the script |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
import cuid2 from "@paralleldrive/cuid2" | ||
|
||
import { db, RoleType } from "~/server/modules/database" | ||
|
||
interface User { | ||
email: string | ||
name?: string | ||
phone?: string | ||
role?: RoleType | ||
} | ||
interface AddUsersToSiteProps { | ||
siteId: number | ||
users: User[] | ||
} | ||
// NOTE: add them as editor first as that is the one with the least permissions | ||
export const addUsersToSite = async ({ | ||
siteId, | ||
users, | ||
}: AddUsersToSiteProps) => { | ||
const processedUsers = users.map(({ email, phone, name, role }) => ({ | ||
email: email.toLowerCase(), | ||
name: name ?? "", | ||
phone: phone ?? "", | ||
role: role ?? RoleType.Editor, | ||
})) | ||
|
||
await Promise.all( | ||
processedUsers.map(async ({ role, ...props }) => { | ||
await db.transaction().execute(async (tx) => { | ||
const user = await tx | ||
.insertInto("User") | ||
.values({ | ||
id: cuid2.createId(), | ||
...props, | ||
}) | ||
.onConflict((oc) => | ||
oc | ||
.column("email") | ||
.doUpdateSet((eb) => ({ email: eb.ref("excluded.email") })), | ||
) | ||
.returning(["id", "name", "email"]) | ||
.executeTakeFirstOrThrow() | ||
|
||
await tx | ||
.insertInto("ResourcePermission") | ||
.values({ | ||
userId: user.id, | ||
siteId, | ||
role, | ||
}) | ||
.execute() | ||
|
||
console.log(`User added: ${user.email} with id: ${user.id}`) | ||
}) | ||
}), | ||
) | ||
} | ||
|
||
// NOTE: Update the list of users and siteId here before executing! | ||
const users: User[] = [] | ||
const siteId = -1 | ||
|
||
await addUsersToSite({ siteId, users }) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,241 @@ | ||
import type { IsomerSchema } from "@opengovsg/isomer-components" | ||
import { ISOMER_ADMINS, ISOMER_MIGRATORS } from "~prisma/constants" | ||
|
||
import type { Navbar } from "~/server/modules/resource/resource.types" | ||
import { db, jsonb, RoleType } from "~/server/modules/database" | ||
import { addUsersToSite } from "./addUsersToSite" | ||
|
||
const PAGE_BLOB: IsomerSchema = { | ||
version: "0.1.0", | ||
layout: "homepage", | ||
page: {}, | ||
content: [ | ||
{ | ||
type: "hero", | ||
variant: "gradient", | ||
title: "Isomer", | ||
subtitle: | ||
"A leading global city of enterprise and talent, a vibrant nation of innovation and opportunity", | ||
buttonLabel: "Main CTA", | ||
buttonUrl: "/", | ||
secondaryButtonLabel: "Sub CTA", | ||
secondaryButtonUrl: "/", | ||
backgroundUrl: "https://ohno.isomer.gov.sg/images/hero-banner.png", | ||
}, | ||
{ | ||
type: "infobar", | ||
title: "This is an infobar", | ||
description: "This is the description that goes into the Infobar section", | ||
}, | ||
{ | ||
type: "infopic", | ||
title: "This is an infopic", | ||
description: "This is the description for the infopic component", | ||
imageSrc: "https://placehold.co/600x400", | ||
}, | ||
{ | ||
type: "keystatistics", | ||
statistics: [ | ||
{ | ||
label: "Average all nighters pulled in a typical calendar month", | ||
value: "3", | ||
}, | ||
{ | ||
label: "Growth in tasks assigned Q4 2024 (YoY)", | ||
value: "+12.2%", | ||
}, | ||
{ | ||
label: "Creative blocks met per single evening", | ||
value: "89", | ||
}, | ||
{ | ||
value: "4.0", | ||
label: "Number of lies in this stat block", | ||
}, | ||
], | ||
title: "Irrationality in numbers", | ||
}, | ||
], | ||
} | ||
const NAV_BAR_ITEMS: Navbar["items"] = [ | ||
{ | ||
name: "Expandable nav item", | ||
url: "/item-one", | ||
items: [ | ||
{ | ||
name: "PA's network one", | ||
url: "/item-one/pa-network-one", | ||
description: "Click here and brace yourself for mild disappointment.", | ||
}, | ||
{ | ||
name: "PA's network two", | ||
url: "/item-one/pa-network-two", | ||
description: "Click here and brace yourself for mild disappointment.", | ||
}, | ||
{ | ||
name: "PA's network three", | ||
url: "/item-one/pa-network-three", | ||
}, | ||
{ | ||
name: "PA's network four", | ||
url: "/item-one/pa-network-four", | ||
description: | ||
"Click here and brace yourself for mild disappointment. This one has a pretty long one", | ||
}, | ||
{ | ||
name: "PA's network five", | ||
url: "/item-one/pa-network-five", | ||
description: | ||
"Click here and brace yourself for mild disappointment. This one has a pretty long one", | ||
}, | ||
{ | ||
name: "PA's network six", | ||
url: "/item-one/pa-network-six", | ||
description: "Click here and brace yourself for mild disappointment.", | ||
}, | ||
], | ||
}, | ||
] | ||
const FOOTER_ITEMS = [ | ||
{ | ||
title: "About us", | ||
url: "/about", | ||
}, | ||
{ | ||
title: "Our partners", | ||
url: "/partners", | ||
}, | ||
{ | ||
title: "Grants and programmes", | ||
url: "/grants-and-programmes", | ||
}, | ||
{ | ||
title: "Contact us", | ||
url: "/contact-us", | ||
}, | ||
{ | ||
title: "Something else", | ||
url: "/something-else", | ||
}, | ||
{ | ||
title: "Resources", | ||
url: "/resources", | ||
}, | ||
] | ||
|
||
const FOOTER = { | ||
contactUsLink: "/contact-us", | ||
feedbackFormLink: "https://www.form.gov.sg", | ||
privacyStatementLink: "/privacy", | ||
termsOfUseLink: "/terms-of-use", | ||
siteNavItems: FOOTER_ITEMS, | ||
} | ||
|
||
interface CreateSiteProps { | ||
siteName: string | ||
} | ||
export const createSite = async ({ siteName }: CreateSiteProps) => { | ||
const siteId = await db.transaction().execute(async (tx) => { | ||
const { id: siteId } = await tx | ||
.insertInto("Site") | ||
.values({ | ||
name: siteName, | ||
theme: jsonb({ | ||
colors: { | ||
brand: { | ||
canvas: { | ||
alt: "#bfcfd7", | ||
default: "#e6ecef", | ||
inverse: "#00405f", | ||
backdrop: "#80a0af", | ||
}, | ||
interaction: { | ||
hover: "#002e44", | ||
default: "#00405f", | ||
pressed: "#00283b", | ||
}, | ||
}, | ||
}, | ||
}), | ||
config: jsonb({ | ||
theme: "isomer-next", | ||
siteName: "MTI", | ||
logoUrl: "https://www.isomer.gov.sg/images/isomer-logo.svg", | ||
search: undefined, | ||
isGovernment: true, | ||
}), | ||
}) | ||
.onConflict((oc) => | ||
oc | ||
.column("name") | ||
.doUpdateSet((eb) => ({ name: eb.ref("excluded.name") })), | ||
) | ||
.returning("id") | ||
.executeTakeFirstOrThrow() | ||
|
||
await tx | ||
.insertInto("Footer") | ||
.values({ | ||
siteId, | ||
content: jsonb(FOOTER), | ||
}) | ||
.onConflict((oc) => | ||
oc | ||
.column("siteId") | ||
.doUpdateSet((eb) => ({ siteId: eb.ref("excluded.siteId") })), | ||
) | ||
.execute() | ||
|
||
await tx | ||
.insertInto("Navbar") | ||
.values({ | ||
siteId, | ||
content: jsonb(NAV_BAR_ITEMS), | ||
}) | ||
.onConflict((oc) => | ||
oc | ||
.column("siteId") | ||
.doUpdateSet((eb) => ({ siteId: eb.ref("excluded.siteId") })), | ||
) | ||
.execute() | ||
|
||
const { id: blobId } = await tx | ||
.insertInto("Blob") | ||
.values({ content: jsonb(PAGE_BLOB) }) | ||
.returning("id") | ||
.executeTakeFirstOrThrow() | ||
|
||
await tx | ||
.insertInto("Resource") | ||
.values({ | ||
draftBlobId: String(blobId), | ||
permalink: "", | ||
siteId, | ||
type: "RootPage", | ||
title: "Home", | ||
}) | ||
|
||
.onConflict((oc) => | ||
oc.column("draftBlobId").doUpdateSet((eb) => ({ | ||
draftBlobId: eb.ref("excluded.draftBlobId"), | ||
})), | ||
) | ||
.executeTakeFirstOrThrow() | ||
|
||
console.log(`Added site ${siteName} with id ${siteId} to database`) | ||
return siteId | ||
}) | ||
|
||
await addUsersToSite({ | ||
siteId, | ||
users: [...ISOMER_ADMINS, ...ISOMER_MIGRATORS].map((email) => ({ | ||
email: `${email}@open.gov.sg`, | ||
role: RoleType.Admin, | ||
})), | ||
}) | ||
|
||
return siteId | ||
} | ||
|
||
// NOTE: Update the site name here before executing! | ||
await createSite({ siteName: "" }) |
Oops, something went wrong.