feat: add card labels

This commit is contained in:
Henry
2023-12-21 17:43:40 +00:00
parent 0c6140921d
commit 0116a1ae2c
13 changed files with 1685 additions and 37 deletions

View File

@@ -28,7 +28,7 @@ const Modal: React.FC<Props> = ({ children }) => {
</Transition.Child>
<div className="fixed inset-0 z-10 w-screen overflow-y-auto">
<div className="flex min-h-full items-end justify-center p-4 text-center sm:items-center sm:p-0">
<div className="flex min-h-full items-start justify-center p-4 text-center sm:items-start sm:p-0">
<Transition.Child
as={Fragment}
enter="ease-out duration-300"
@@ -38,7 +38,7 @@ const Modal: React.FC<Props> = ({ children }) => {
leaveFrom="opacity-100 translate-y-0 sm:scale-100"
leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
>
<Dialog.Panel className="relative transform overflow-hidden rounded-lg border border-dark-400 bg-dark-100 px-4 pb-4 pt-5 text-left shadow-xl transition-all sm:my-8 sm:w-full sm:max-w-sm sm:p-6">
<Dialog.Panel className="relative mt-[25vh] transform overflow-hidden rounded-lg border border-dark-400 bg-dark-100 px-4 pb-4 pt-5 text-left shadow-xl transition-all sm:mb-8 sm:w-full sm:max-w-sm sm:p-6">
{children}
</Dialog.Panel>
</Transition.Child>

View File

@@ -30,6 +30,17 @@ interface List {
interface Card {
publicId: string;
title: string;
labels?: Label[];
}
interface Label {
label: LabelData;
}
interface LabelData {
publicId: string;
name: string;
colourCode: string;
}
interface FormValues {
@@ -226,12 +237,32 @@ export default function BoardPage() {
<Link
key={card.publicId}
href={`/cards/${card.publicId}`}
className="mb-2 flex !cursor-pointer rounded-md border border-dark-200 bg-dark-500 px-3 py-2 text-sm text-dark-1000"
className="mb-2 flex !cursor-pointer flex-col rounded-md border border-dark-200 bg-dark-500 px-3 py-2 text-sm text-dark-1000"
ref={provided.innerRef}
{...provided.draggableProps}
{...provided.dragHandleProps}
>
{card.title}
<div>{card.title}</div>
{card.labels?.length ? (
<div className="mt-2 flex justify-end space-x-1">
{card.labels.map(({ label }) => (
<span
key={label.publicId}
className="inline-flex w-fit items-center gap-x-1.5 rounded-full px-2 py-1 text-[10px] font-medium text-dark-1000 ring-1 ring-inset ring-dark-800"
>
<svg
fill={label.colourCode}
className="h-2 w-2"
viewBox="0 0 6 6"
aria-hidden="true"
>
<circle cx={3} cy={3} r={3} />
</svg>
<div>{label.name}</div>
</span>
))}
</div>
) : null}
</Link>
)}
</Draggable>

View File

@@ -10,7 +10,7 @@ export default function Dropdown() {
<Menu as="div" className="relative inline-block text-left">
<div>
<Menu.Button className="flex h-8 w-8 items-center justify-center rounded-[5px] hover:bg-dark-200">
<HiEllipsisHorizontal size={25} color="white" />
<HiEllipsisHorizontal size={25} className="text-dark-900" />
</Menu.Button>
</div>

View File

@@ -0,0 +1,140 @@
import { Fragment } from "react";
import { api } from "~/trpc/react";
import { Menu, Transition } from "@headlessui/react";
import { HiMiniPlus } from "react-icons/hi2";
import { useFormik } from "formik";
interface LabelSelectorProps {
cardPublicId: string;
labels: {
publicId: string;
name: string;
selected: boolean;
colourCode: string;
}[];
}
export default function LabelSelector({
cardPublicId,
labels,
}: LabelSelectorProps) {
const utils = api.useUtils();
const refetchCard = () => utils.card.byId.refetch({ id: cardPublicId });
const addOrRemoveLabel = api.card.addOrRemoveLabel.useMutation({
onSuccess: async () => {
await refetchCard();
},
});
const formik = useFormik({
initialValues: {
...Object.fromEntries(
labels?.map((label) => [label.publicId, label.selected]) ?? [],
),
},
onSubmit: (values) => {
console.log({ values });
},
enableReinitialize: true,
});
const selectedLabels = labels.filter((label) => label.selected);
return (
<>
<Menu
as="div"
className="relative flex w-full flex-wrap items-center text-left"
>
{selectedLabels.length ? (
<>
{selectedLabels.map((label) => (
<Menu.Button
key={label.publicId}
className="my-1 mr-2 inline-flex w-fit items-center gap-x-1.5 rounded-full px-2 py-1 text-[12px] font-medium text-dark-1000 ring-1 ring-inset ring-dark-800"
>
<svg
fill={label.colourCode}
className="h-2 w-2"
viewBox="0 0 6 6"
aria-hidden="true"
>
<circle cx={3} cy={3} r={3} />
</svg>
<div>{label.name}</div>
</Menu.Button>
))}
<Menu.Button className="my-1 inline-flex w-fit items-center gap-x-1.5 rounded-full py-1 pl-2 pr-4 text-[12px] font-medium text-dark-800 ring-inset ring-dark-800 hover:bg-dark-200">
<HiMiniPlus size={16} />
Add label
</Menu.Button>
</>
) : (
<Menu.Button className="flex h-full w-full items-center rounded-[5px] border-[1px] border-dark-100 pl-2 text-left text-sm hover:border-dark-300 hover:bg-dark-200">
<HiMiniPlus size={22} className="pr-2" />
Add label
</Menu.Button>
)}
<Transition
as={Fragment}
enter="transition ease-out duration-100"
enterFrom="transform opacity-0 scale-95"
enterTo="transform opacity-100 scale-100"
leave="transition ease-in duration-75"
leaveFrom="transform opacity-100 scale-100"
leaveTo="transform opacity-0 scale-95"
>
<Menu.Items className="absolute right-[200px] top-[30px] z-10 mt-2 w-56 origin-top-right rounded-md border-[1px] border-dark-500 bg-dark-200 shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none">
<div className="p-2">
<form onSubmit={formik.handleSubmit}>
{labels?.map((label) => (
<Menu.Item key={label.publicId}>
{() => (
<div
key={label.publicId}
className="flex items-center rounded-[5px] p-2 hover:bg-dark-300"
onClick={async (e) => {
e.preventDefault();
await formik.setFieldValue(
label.publicId,
!formik.values[label.publicId],
);
addOrRemoveLabel.mutate({
cardPublicId,
labelPublicId: label.publicId,
});
await formik.submitForm();
}}
>
<input
id={label.publicId}
name={label.publicId}
type="checkbox"
className="h-[14px] w-[14px] rounded bg-transparent"
onClick={(event) => event.stopPropagation()}
onChange={formik.handleChange}
checked={formik.values[label.publicId]}
/>
<label
htmlFor={label.publicId}
className="ml-3 text-sm text-gray-500"
>
{label.name}
</label>
</div>
)}
</Menu.Item>
))}
</form>
</div>
</Menu.Items>
</Transition>
</Menu>
</>
);
}

View File

@@ -6,11 +6,13 @@ import ContentEditable from "react-contenteditable";
import Dropdown from "./components/Dropdown";
import { DeleteCardConfirmation } from "./components/DeleteCardConfirmation";
import LabelSelector from "./components/LabelSelector";
import Modal from "~/app/_components/modal";
import { useModal } from "~/app/providers/modal";
import { api } from "~/trpc/react";
interface FormValues {
cardId: string;
title: string;
@@ -26,6 +28,21 @@ export default function CardPage() {
const { data } = api.card.byId.useQuery({ id: cardId ?? "" });
const boardId = data?.list?.board?.publicId;
const labels = data?.list?.board?.labels;
const selectedLabels = data?.labels;
const formattedLabels =
labels?.map((label) => {
const isSelected = selectedLabels?.some(
(selectedLabel) => selectedLabel.label.publicId === label.publicId,
);
return {
...label,
selected: isSelected ?? false,
colourCode: label.colourCode ?? "",
};
}) ?? [];
const updateCard = api.card.update.useMutation();
@@ -48,40 +65,49 @@ export default function CardPage() {
if (!cardId) return <></>;
return (
<div className="p-8">
<div className="mb-8 flex w-full justify-between">
<form onSubmit={formik.handleSubmit} className="w-full space-y-6">
<div>
<input
type="text"
id="title"
name="title"
value={formik.values.title}
onChange={formik.handleChange}
onBlur={formik.submitForm}
className="block w-full border-0 bg-transparent p-0 py-1.5 font-medium tracking-tight text-dark-1000 focus:ring-0 sm:text-[1.2rem] sm:leading-6"
/>
<div className="flex h-full flex-1 flex-row">
<div className="w-full p-8">
<div className="mb-8 flex w-full justify-between">
<form onSubmit={formik.handleSubmit} className="w-full space-y-6">
<div>
<input
type="text"
id="title"
name="title"
value={formik.values.title}
onChange={formik.handleChange}
onBlur={formik.submitForm}
className="block w-full border-0 bg-transparent p-0 py-0 font-medium tracking-tight text-dark-1000 focus:ring-0 sm:text-[1.2rem] sm:leading-6"
/>
</div>
</form>
<div className="flex">
<Dropdown />
</div>
</form>
<div className="flex">
<Dropdown />
</div>
<div className="mb-8 flex w-full max-w-2xl justify-between">
<form onSubmit={formik.handleSubmit} className="w-full space-y-6">
<div className="mt-2">
<ContentEditable
html={formik.values.description || "Add description..."}
disabled={false}
onChange={(e) =>
formik.setFieldValue("description", e.target.value)
}
onBlur={formik.submitForm}
className="block w-full border-0 bg-transparent py-1.5 text-dark-1000 focus-visible:outline-none sm:text-sm sm:leading-6"
/>
</div>
</form>
</div>
</div>
<div className="mb-8 flex w-full max-w-2xl justify-between">
<form onSubmit={formik.handleSubmit} className="w-full space-y-6">
<div className="mt-2">
<ContentEditable
html={formik.values.description || "Add description..."}
disabled={false}
onChange={(e) =>
formik.setFieldValue("description", e.target.value)
}
onBlur={formik.submitForm}
className="block w-full border-0 bg-transparent py-1.5 text-dark-1000 focus-visible:outline-none sm:text-sm sm:leading-6"
/>
</div>
</form>
<div className="min-w-[325px] border-l-[1px] border-dark-400 bg-dark-100 p-8 text-dark-900">
<div className="flex w-full">
<p className="my-2 pr-8 text-sm">Labels</p>
<LabelSelector cardPublicId={cardId} labels={formattedLabels} />
</div>
</div>
<Modal>
{modalContentType === "DELETE_CARD" && (
<DeleteCardConfirmation

View File

@@ -51,6 +51,23 @@ export const boardRouter = createTRPCRouter({
description: true,
listId: true,
index: true,
},
with: {
labels: {
columns: {
labelId: false,
cardId: false,
},
with: {
label: {
columns: {
publicId: true,
name: true,
colourCode: true,
}
}
}
}
}
}
},

View File

@@ -1,7 +1,7 @@
import { z } from "zod";
import { and, desc, eq, isNull, sql } from "drizzle-orm";
import { cards, lists } from "~/server/db/schema";
import { cards, cardsToLabels, labels, lists } from "~/server/db/schema";
import { generateUID } from "~/utils/generateUID";
import {
@@ -49,11 +49,63 @@ export const cardRouter = createTRPCRouter({
});
})
}),
addOrRemoveLabel: publicProcedure
.input(
z.object({
cardPublicId: z.string().min(12),
labelPublicId: z.string().min(12),
}),
)
.mutation(({ ctx, input }) => {
const userId = ctx.session?.user.id;
if (!userId) return;
return ctx.db.transaction(async (tx) => {
const card = await tx.query.cards.findFirst({
where: and(eq(cards.publicId, input.cardPublicId), isNull(cards.deletedAt)),
});
const label = await tx.query.labels.findFirst({
where: eq(labels.publicId, input.labelPublicId),
});
if (!card || !label) return;
const labelExists = await tx.query.cardsToLabels.findFirst({
where: and(eq(cardsToLabels.cardId, card.id), eq(cardsToLabels.labelId, label.id)),
});
if (labelExists) {
return tx.delete(cardsToLabels).where(and(eq(cardsToLabels.cardId, card.id), eq(cardsToLabels.labelId, label.id)),);
}
return tx.insert(cardsToLabels).values({
cardId: card.id,
labelId: label.id,
});
})
}),
byId: publicProcedure
.input(z.object({ id: z.string().min(12) }))
.query(({ ctx, input }) =>
ctx.db.query.cards.findFirst({
with: {
labels: {
columns: {
labelId: false,
cardId: false,
},
with: {
label: {
columns: {
publicId: true,
name: true,
colourCode: true,
}
}
}
},
list: {
columns: {
publicId: true,
@@ -63,6 +115,15 @@ export const cardRouter = createTRPCRouter({
columns: {
publicId: true,
},
with: {
labels: {
columns: {
publicId: true,
colourCode: true,
name: true,
}
}
}
}
}
},

View File

@@ -0,0 +1,20 @@
CREATE TABLE `card_label` (
`cardId` bigint NOT NULL,
`labelId` bigint NOT NULL
);
--> statement-breakpoint
CREATE TABLE `label` (
`id` bigint AUTO_INCREMENT NOT NULL,
`publicId` varchar(12) NOT NULL,
`name` varchar(256) NOT NULL,
`colourCode` varchar(12),
`createdBy` varchar(256) NOT NULL,
`createdAt` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updatedAt` timestamp ON UPDATE CURRENT_TIMESTAMP,
`boardId` bigint NOT NULL,
CONSTRAINT `label_id` PRIMARY KEY(`id`),
CONSTRAINT `label_publicId_unique` UNIQUE(`publicId`)
);
--> statement-breakpoint
-- ALTER TABLE `card_label` ADD CONSTRAINT `card_label_cardId_card_id_fk` FOREIGN KEY (`cardId`) REFERENCES `card`(`id`) ON DELETE no action ON UPDATE no action;--> statement-breakpoint
-- ALTER TABLE `card_label` ADD CONSTRAINT `card_label_labelId_label_id_fk` FOREIGN KEY (`labelId`) REFERENCES `label`(`id`) ON DELETE no action ON UPDATE no action;

View File

@@ -0,0 +1 @@
ALTER TABLE `card_label` ADD PRIMARY KEY(`cardId`,`labelId`);

View File

@@ -0,0 +1,639 @@
{
"version": "5",
"dialect": "mysql",
"id": "cb5d6d0a-3ad3-45b1-81a4-02844b130ebf",
"prevId": "a623c303-6c0b-416d-aa46-2c0a9e2328cd",
"tables": {
"account": {
"name": "account",
"columns": {
"userId": {
"name": "userId",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"type": {
"name": "type",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"provider": {
"name": "provider",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"providerAccountId": {
"name": "providerAccountId",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"refresh_token": {
"name": "refresh_token",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"access_token": {
"name": "access_token",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"expires_at": {
"name": "expires_at",
"type": "int",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"token_type": {
"name": "token_type",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"scope": {
"name": "scope",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"id_token": {
"name": "id_token",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"session_state": {
"name": "session_state",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
}
},
"indexes": {
"userId_idx": {
"name": "userId_idx",
"columns": [
"userId"
],
"isUnique": false
}
},
"foreignKeys": {},
"compositePrimaryKeys": {
"account_provider_providerAccountId": {
"name": "account_provider_providerAccountId",
"columns": [
"provider",
"providerAccountId"
]
}
},
"uniqueConstraints": {}
},
"board": {
"name": "board",
"columns": {
"id": {
"name": "id",
"type": "bigint",
"primaryKey": false,
"notNull": true,
"autoincrement": true
},
"publicId": {
"name": "publicId",
"type": "varchar(12)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"name": {
"name": "name",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"createdBy": {
"name": "createdBy",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"createdAt": {
"name": "createdAt",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "CURRENT_TIMESTAMP"
},
"updatedAt": {
"name": "updatedAt",
"type": "timestamp",
"primaryKey": false,
"notNull": false,
"autoincrement": false,
"onUpdate": true
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {
"board_id": {
"name": "board_id",
"columns": [
"id"
]
}
},
"uniqueConstraints": {
"board_publicId_unique": {
"name": "board_publicId_unique",
"columns": [
"publicId"
]
}
}
},
"card": {
"name": "card",
"columns": {
"id": {
"name": "id",
"type": "bigint",
"primaryKey": false,
"notNull": true,
"autoincrement": true
},
"publicId": {
"name": "publicId",
"type": "varchar(12)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"title": {
"name": "title",
"type": "varchar(256)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"description": {
"name": "description",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"createdBy": {
"name": "createdBy",
"type": "varchar(256)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"createdAt": {
"name": "createdAt",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "CURRENT_TIMESTAMP"
},
"updatedAt": {
"name": "updatedAt",
"type": "timestamp",
"primaryKey": false,
"notNull": false,
"autoincrement": false,
"onUpdate": true
},
"listId": {
"name": "listId",
"type": "bigint",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"index": {
"name": "index",
"type": "int",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"deletedAt": {
"name": "deletedAt",
"type": "timestamp",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"deletedBy": {
"name": "deletedBy",
"type": "varchar(256)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {
"card_id": {
"name": "card_id",
"columns": [
"id"
]
}
},
"uniqueConstraints": {
"card_publicId_unique": {
"name": "card_publicId_unique",
"columns": [
"publicId"
]
}
}
},
"card_label": {
"name": "card_label",
"columns": {
"cardId": {
"name": "cardId",
"type": "bigint",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"labelId": {
"name": "labelId",
"type": "bigint",
"primaryKey": false,
"notNull": true,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {
"card_label_cardId_card_id_fk": {
"name": "card_label_cardId_card_id_fk",
"tableFrom": "card_label",
"tableTo": "card",
"columnsFrom": [
"cardId"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
},
"card_label_labelId_label_id_fk": {
"name": "card_label_labelId_label_id_fk",
"tableFrom": "card_label",
"tableTo": "label",
"columnsFrom": [
"labelId"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"label": {
"name": "label",
"columns": {
"id": {
"name": "id",
"type": "bigint",
"primaryKey": false,
"notNull": true,
"autoincrement": true
},
"publicId": {
"name": "publicId",
"type": "varchar(12)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"name": {
"name": "name",
"type": "varchar(256)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"colourCode": {
"name": "colourCode",
"type": "varchar(12)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"createdBy": {
"name": "createdBy",
"type": "varchar(256)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"createdAt": {
"name": "createdAt",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "CURRENT_TIMESTAMP"
},
"updatedAt": {
"name": "updatedAt",
"type": "timestamp",
"primaryKey": false,
"notNull": false,
"autoincrement": false,
"onUpdate": true
},
"boardId": {
"name": "boardId",
"type": "bigint",
"primaryKey": false,
"notNull": true,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {
"label_id": {
"name": "label_id",
"columns": [
"id"
]
}
},
"uniqueConstraints": {
"label_publicId_unique": {
"name": "label_publicId_unique",
"columns": [
"publicId"
]
}
}
},
"list": {
"name": "list",
"columns": {
"id": {
"name": "id",
"type": "bigint",
"primaryKey": false,
"notNull": true,
"autoincrement": true
},
"publicId": {
"name": "publicId",
"type": "varchar(12)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"name": {
"name": "name",
"type": "varchar(256)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"createdBy": {
"name": "createdBy",
"type": "varchar(256)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"createdAt": {
"name": "createdAt",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "CURRENT_TIMESTAMP"
},
"updatedAt": {
"name": "updatedAt",
"type": "timestamp",
"primaryKey": false,
"notNull": false,
"autoincrement": false,
"onUpdate": true
},
"boardId": {
"name": "boardId",
"type": "bigint",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"index": {
"name": "index",
"type": "int",
"primaryKey": false,
"notNull": true,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {
"list_id": {
"name": "list_id",
"columns": [
"id"
]
}
},
"uniqueConstraints": {
"list_publicId_unique": {
"name": "list_publicId_unique",
"columns": [
"publicId"
]
}
}
},
"session": {
"name": "session",
"columns": {
"sessionToken": {
"name": "sessionToken",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"userId": {
"name": "userId",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"expires": {
"name": "expires",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"autoincrement": false
}
},
"indexes": {
"userId_idx": {
"name": "userId_idx",
"columns": [
"userId"
],
"isUnique": false
}
},
"foreignKeys": {},
"compositePrimaryKeys": {
"session_sessionToken": {
"name": "session_sessionToken",
"columns": [
"sessionToken"
]
}
},
"uniqueConstraints": {}
},
"user": {
"name": "user",
"columns": {
"id": {
"name": "id",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"name": {
"name": "name",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"email": {
"name": "email",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"emailVerified": {
"name": "emailVerified",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": false,
"autoincrement": false,
"default": "CURRENT_TIMESTAMP(3)"
},
"image": {
"name": "image",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {
"user_id": {
"name": "user_id",
"columns": [
"id"
]
}
},
"uniqueConstraints": {
"user_email_unique": {
"name": "user_email_unique",
"columns": [
"email"
]
}
}
},
"verificationToken": {
"name": "verificationToken",
"columns": {
"identifier": {
"name": "identifier",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"token": {
"name": "token",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"expires": {
"name": "expires",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {
"verificationToken_identifier_token": {
"name": "verificationToken_identifier_token",
"columns": [
"identifier",
"token"
]
}
},
"uniqueConstraints": {}
}
},
"schemas": {},
"_meta": {
"schemas": {},
"tables": {},
"columns": {}
}
}

View File

@@ -0,0 +1,647 @@
{
"version": "5",
"dialect": "mysql",
"id": "52bf8e38-66ad-4bb6-9924-798177dbd1b5",
"prevId": "cb5d6d0a-3ad3-45b1-81a4-02844b130ebf",
"tables": {
"account": {
"name": "account",
"columns": {
"userId": {
"name": "userId",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"type": {
"name": "type",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"provider": {
"name": "provider",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"providerAccountId": {
"name": "providerAccountId",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"refresh_token": {
"name": "refresh_token",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"access_token": {
"name": "access_token",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"expires_at": {
"name": "expires_at",
"type": "int",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"token_type": {
"name": "token_type",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"scope": {
"name": "scope",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"id_token": {
"name": "id_token",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"session_state": {
"name": "session_state",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
}
},
"indexes": {
"userId_idx": {
"name": "userId_idx",
"columns": [
"userId"
],
"isUnique": false
}
},
"foreignKeys": {},
"compositePrimaryKeys": {
"account_provider_providerAccountId": {
"name": "account_provider_providerAccountId",
"columns": [
"provider",
"providerAccountId"
]
}
},
"uniqueConstraints": {}
},
"board": {
"name": "board",
"columns": {
"id": {
"name": "id",
"type": "bigint",
"primaryKey": false,
"notNull": true,
"autoincrement": true
},
"publicId": {
"name": "publicId",
"type": "varchar(12)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"name": {
"name": "name",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"createdBy": {
"name": "createdBy",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"createdAt": {
"name": "createdAt",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "CURRENT_TIMESTAMP"
},
"updatedAt": {
"name": "updatedAt",
"type": "timestamp",
"primaryKey": false,
"notNull": false,
"autoincrement": false,
"onUpdate": true
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {
"board_id": {
"name": "board_id",
"columns": [
"id"
]
}
},
"uniqueConstraints": {
"board_publicId_unique": {
"name": "board_publicId_unique",
"columns": [
"publicId"
]
}
}
},
"card": {
"name": "card",
"columns": {
"id": {
"name": "id",
"type": "bigint",
"primaryKey": false,
"notNull": true,
"autoincrement": true
},
"publicId": {
"name": "publicId",
"type": "varchar(12)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"title": {
"name": "title",
"type": "varchar(256)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"description": {
"name": "description",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"createdBy": {
"name": "createdBy",
"type": "varchar(256)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"createdAt": {
"name": "createdAt",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "CURRENT_TIMESTAMP"
},
"updatedAt": {
"name": "updatedAt",
"type": "timestamp",
"primaryKey": false,
"notNull": false,
"autoincrement": false,
"onUpdate": true
},
"listId": {
"name": "listId",
"type": "bigint",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"index": {
"name": "index",
"type": "int",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"deletedAt": {
"name": "deletedAt",
"type": "timestamp",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"deletedBy": {
"name": "deletedBy",
"type": "varchar(256)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {
"card_id": {
"name": "card_id",
"columns": [
"id"
]
}
},
"uniqueConstraints": {
"card_publicId_unique": {
"name": "card_publicId_unique",
"columns": [
"publicId"
]
}
}
},
"card_label": {
"name": "card_label",
"columns": {
"cardId": {
"name": "cardId",
"type": "bigint",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"labelId": {
"name": "labelId",
"type": "bigint",
"primaryKey": false,
"notNull": true,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {
"card_label_cardId_card_id_fk": {
"name": "card_label_cardId_card_id_fk",
"tableFrom": "card_label",
"tableTo": "card",
"columnsFrom": [
"cardId"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
},
"card_label_labelId_label_id_fk": {
"name": "card_label_labelId_label_id_fk",
"tableFrom": "card_label",
"tableTo": "label",
"columnsFrom": [
"labelId"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {
"card_label_cardId_labelId": {
"name": "card_label_cardId_labelId",
"columns": [
"cardId",
"labelId"
]
}
},
"uniqueConstraints": {}
},
"label": {
"name": "label",
"columns": {
"id": {
"name": "id",
"type": "bigint",
"primaryKey": false,
"notNull": true,
"autoincrement": true
},
"publicId": {
"name": "publicId",
"type": "varchar(12)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"name": {
"name": "name",
"type": "varchar(256)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"colourCode": {
"name": "colourCode",
"type": "varchar(12)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"createdBy": {
"name": "createdBy",
"type": "varchar(256)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"createdAt": {
"name": "createdAt",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "CURRENT_TIMESTAMP"
},
"updatedAt": {
"name": "updatedAt",
"type": "timestamp",
"primaryKey": false,
"notNull": false,
"autoincrement": false,
"onUpdate": true
},
"boardId": {
"name": "boardId",
"type": "bigint",
"primaryKey": false,
"notNull": true,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {
"label_id": {
"name": "label_id",
"columns": [
"id"
]
}
},
"uniqueConstraints": {
"label_publicId_unique": {
"name": "label_publicId_unique",
"columns": [
"publicId"
]
}
}
},
"list": {
"name": "list",
"columns": {
"id": {
"name": "id",
"type": "bigint",
"primaryKey": false,
"notNull": true,
"autoincrement": true
},
"publicId": {
"name": "publicId",
"type": "varchar(12)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"name": {
"name": "name",
"type": "varchar(256)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"createdBy": {
"name": "createdBy",
"type": "varchar(256)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"createdAt": {
"name": "createdAt",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "CURRENT_TIMESTAMP"
},
"updatedAt": {
"name": "updatedAt",
"type": "timestamp",
"primaryKey": false,
"notNull": false,
"autoincrement": false,
"onUpdate": true
},
"boardId": {
"name": "boardId",
"type": "bigint",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"index": {
"name": "index",
"type": "int",
"primaryKey": false,
"notNull": true,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {
"list_id": {
"name": "list_id",
"columns": [
"id"
]
}
},
"uniqueConstraints": {
"list_publicId_unique": {
"name": "list_publicId_unique",
"columns": [
"publicId"
]
}
}
},
"session": {
"name": "session",
"columns": {
"sessionToken": {
"name": "sessionToken",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"userId": {
"name": "userId",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"expires": {
"name": "expires",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"autoincrement": false
}
},
"indexes": {
"userId_idx": {
"name": "userId_idx",
"columns": [
"userId"
],
"isUnique": false
}
},
"foreignKeys": {},
"compositePrimaryKeys": {
"session_sessionToken": {
"name": "session_sessionToken",
"columns": [
"sessionToken"
]
}
},
"uniqueConstraints": {}
},
"user": {
"name": "user",
"columns": {
"id": {
"name": "id",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"name": {
"name": "name",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"email": {
"name": "email",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"emailVerified": {
"name": "emailVerified",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": false,
"autoincrement": false,
"default": "CURRENT_TIMESTAMP(3)"
},
"image": {
"name": "image",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {
"user_id": {
"name": "user_id",
"columns": [
"id"
]
}
},
"uniqueConstraints": {
"user_email_unique": {
"name": "user_email_unique",
"columns": [
"email"
]
}
}
},
"verificationToken": {
"name": "verificationToken",
"columns": {
"identifier": {
"name": "identifier",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"token": {
"name": "token",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"expires": {
"name": "expires",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {
"verificationToken_identifier_token": {
"name": "verificationToken_identifier_token",
"columns": [
"identifier",
"token"
]
}
},
"uniqueConstraints": {}
}
},
"schemas": {},
"_meta": {
"schemas": {},
"tables": {},
"columns": {}
}
}

View File

@@ -22,6 +22,20 @@
"when": 1702630369122,
"tag": "0002_colossal_randall",
"breakpoints": true
},
{
"idx": 3,
"version": "5",
"when": 1702750430064,
"tag": "0003_heavy_thanos",
"breakpoints": true
},
{
"idx": 4,
"version": "5",
"when": 1702924641678,
"tag": "0004_tidy_lord_hawal",
"breakpoints": true
}
]
}

View File

@@ -39,6 +39,56 @@ export const boardsRelations = relations(boards, ({ one, many }) => ({
references: [users.id],
}),
lists: many(lists),
labels: many(labels)
}));
export const labels = mySqlTable(
"label",
{
id: bigint("id", { mode: "number" }).primaryKey().autoincrement(),
publicId: varchar("publicId", { length: 12 }).notNull().unique(),
name: varchar("name", { length: 256 }).notNull(),
colourCode: varchar("colourCode", { length: 12 }),
createdBy: varchar("createdBy", { length: 256 }).notNull(),
createdAt: timestamp("createdAt")
.default(sql`CURRENT_TIMESTAMP`)
.notNull(),
updatedAt: timestamp("updatedAt").onUpdateNow(),
boardId: bigint("boardId", { mode: "number" }).notNull(),
}
);
export const labelsRelations = relations(labels, ({ one, many }) => ({
createdBy: one(users, {
fields: [labels.createdBy],
references: [users.id],
}),
board: one(boards, {
fields: [labels.boardId],
references: [boards.id],
}),
cards: many(cardsToLabels)
}));
export const cardsToLabels = mySqlTable(
"card_label",
{
cardId: bigint("cardId", { mode: "number" }).notNull().references(() => cards.id),
labelId: bigint("labelId", { mode: "number" }).notNull().references(() => labels.id),
}, (t) => ({
pk: primaryKey(t.cardId, t.labelId),
}),
);
export const cardOnLabelsRelations = relations(cardsToLabels, ({ one }) => ({
card: one(cards, {
fields: [cardsToLabels.cardId],
references: [cards.id],
}),
label: one(labels, {
fields: [cardsToLabels.labelId],
references: [labels.id],
}),
}));
export const lists = mySqlTable(
@@ -88,7 +138,7 @@ export const cards = mySqlTable(
}
);
export const cardsRelations = relations(cards, ({ one }) => ({
export const cardsRelations = relations(cards, ({ one, many }) => ({
createdBy: one(users, {
fields: [cards.createdBy],
references: [users.id],
@@ -101,6 +151,8 @@ export const cardsRelations = relations(cards, ({ one }) => ({
fields: [cards.deletedBy],
references: [users.id],
}),
labels: many(cardsToLabels)
}));
export const users = mySqlTable("user", {