diff --git a/apps/api/package.json b/apps/api/package.json index 5243bd8..7f5eb8d 100644 --- a/apps/api/package.json +++ b/apps/api/package.json @@ -71,6 +71,7 @@ "testEnvironment": "node", "moduleNameMapper": { "^@workspace/schema$": "/../test/__mocks__/schema.ts", + "^@workspace/schema/migrations$": "/../test/__mocks__/schema-migrations.ts", "^valibot$": "/../test/__mocks__/valibot.ts" } } diff --git a/apps/api/src/database/drizzle.service.ts b/apps/api/src/database/drizzle.service.ts index 480fc2f..7dd3501 100644 --- a/apps/api/src/database/drizzle.service.ts +++ b/apps/api/src/database/drizzle.service.ts @@ -1,5 +1,6 @@ import { Injectable, OnModuleInit, OnModuleDestroy } from "@nestjs/common"; import { Pool } from "pg"; +import { migrationsDir } from "@workspace/schema/migrations"; import { drizzle, NodePgDatabase, @@ -8,7 +9,6 @@ import { tasks, projectsRelations, tasksRelations, - migrationsDir, } from "@workspace/schema"; const DEFAULT_PG_PORT = 5432; diff --git a/apps/api/src/main.ts b/apps/api/src/main.ts index 1962fc9..fc87c15 100644 --- a/apps/api/src/main.ts +++ b/apps/api/src/main.ts @@ -5,6 +5,7 @@ const DEFAULT_PORT = 3001; async function bootstrap() { const app = await NestFactory.create(AppModule); + app.enableCors(); await app.listen(process.env.PORT ?? DEFAULT_PORT); } // eslint-disable-next-line @typescript-eslint/no-floating-promises diff --git a/apps/api/test/__mocks__/schema-migrations.ts b/apps/api/test/__mocks__/schema-migrations.ts new file mode 100644 index 0000000..0a77362 --- /dev/null +++ b/apps/api/test/__mocks__/schema-migrations.ts @@ -0,0 +1 @@ +export const migrationsDir = "/mock/migrations"; diff --git a/apps/api/test/jest-e2e.json b/apps/api/test/jest-e2e.json index 8f816a6..6b6e73b 100644 --- a/apps/api/test/jest-e2e.json +++ b/apps/api/test/jest-e2e.json @@ -8,6 +8,7 @@ }, "moduleNameMapper": { "^@workspace/schema$": "/__mocks__/schema.ts", + "^@workspace/schema/migrations$": "/__mocks__/schema-migrations.ts", "^valibot$": "/__mocks__/valibot.ts" }, "transformIgnorePatterns": [ diff --git a/apps/web/app/app.config.ts b/apps/web/app/app.config.ts index 7420448..65f1645 100644 --- a/apps/web/app/app.config.ts +++ b/apps/web/app/app.config.ts @@ -4,5 +4,8 @@ export default defineAppConfig({ primary: "green", neutral: "slate", }, + main: { + base: "min-h-0 flex-1 overflow-y-auto", + }, }, }); diff --git a/apps/web/app/app.vue b/apps/web/app/app.vue index 534b6be..9548987 100644 --- a/apps/web/app/app.vue +++ b/apps/web/app/app.vue @@ -1,77 +1,64 @@ diff --git a/apps/web/app/components/ProjectForm.vue b/apps/web/app/components/ProjectForm.vue new file mode 100644 index 0000000..600ef7f --- /dev/null +++ b/apps/web/app/components/ProjectForm.vue @@ -0,0 +1,54 @@ + + + diff --git a/apps/web/app/components/TemplateMenu.vue b/apps/web/app/components/TemplateMenu.vue index 2443992..c8018d4 100644 --- a/apps/web/app/components/TemplateMenu.vue +++ b/apps/web/app/components/TemplateMenu.vue @@ -2,48 +2,55 @@ + size="xs"> + }" /> diff --git a/apps/web/app/composables/useProjectApi.ts b/apps/web/app/composables/useProjectApi.ts index 51256c9..c793fa1 100644 --- a/apps/web/app/composables/useProjectApi.ts +++ b/apps/web/app/composables/useProjectApi.ts @@ -1,4 +1,4 @@ -import type { Project } from "@workspace/schema"; +import type { Project, ProjectInsertDTO, ProjectUpdateDTO } from "@workspace/schema"; interface ApiResponse { data: T | null; @@ -47,7 +47,7 @@ export function useProjectApi() { loading.value = false; } - async function createProject(body: Record): Promise { + async function createProject(body: Partial): Promise { loading.value = true; error.value = null; @@ -66,7 +66,7 @@ export function useProjectApi() { return data; } - async function updateProject(id: string, body: Record): Promise { + async function updateProject(id: string, body: Partial): Promise { loading.value = true; error.value = null; @@ -76,7 +76,7 @@ export function useProjectApi() { }); if (data) { - const idx = projects.value.findIndex((p) => p.id === id); + const idx = projects.value.findIndex(p => p.id === id); if (idx !== NOT_FOUND) projects.value[idx] = data; } else { error.value = err; @@ -95,7 +95,7 @@ export function useProjectApi() { if (err) { error.value = err; } else { - projects.value = projects.value.filter((p) => p.id !== id); + projects.value = projects.value.filter(p => p.id !== id); } loading.value = false; diff --git a/apps/web/app/layouts/default.vue b/apps/web/app/layouts/default.vue new file mode 100644 index 0000000..ba4672f --- /dev/null +++ b/apps/web/app/layouts/default.vue @@ -0,0 +1,3 @@ + diff --git a/apps/web/app/pages/projects/[id]/edit.vue b/apps/web/app/pages/projects/[id]/edit.vue new file mode 100644 index 0000000..3f09dd1 --- /dev/null +++ b/apps/web/app/pages/projects/[id]/edit.vue @@ -0,0 +1,62 @@ + + + diff --git a/apps/web/app/pages/projects/index.vue b/apps/web/app/pages/projects/index.vue new file mode 100644 index 0000000..d03620b --- /dev/null +++ b/apps/web/app/pages/projects/index.vue @@ -0,0 +1,117 @@ + + + diff --git a/apps/web/app/pages/projects/new.vue b/apps/web/app/pages/projects/new.vue new file mode 100644 index 0000000..b749d06 --- /dev/null +++ b/apps/web/app/pages/projects/new.vue @@ -0,0 +1,34 @@ + + + diff --git a/apps/web/eslint.config.mjs b/apps/web/eslint.config.mjs index f6ae36e..d58f8e5 100644 --- a/apps/web/eslint.config.mjs +++ b/apps/web/eslint.config.mjs @@ -6,5 +6,9 @@ export default withNuxt({ rules: { ...baseRules, "@stylistic/member-delimiter-style": "off", + "vue/max-attributes-per-line": "off", + "vue/singleline-html-element-content-newline": "off", + "vue/html-closing-bracket-newline": "off", + "vue/no-multiple-template-root": "off", }, },) diff --git a/packages/schema/package.json b/packages/schema/package.json index 37364ce..9643db2 100644 --- a/packages/schema/package.json +++ b/packages/schema/package.json @@ -10,6 +10,16 @@ "types": "./dist/index.d.ts", "import": "./dist/index.js", "default": "./dist/index.js" + }, + "./constants": { + "types": "./src/constants.ts", + "import": "./src/constants.ts", + "default": "./src/constants.ts" + }, + "./migrations": { + "types": "./dist/migrations.d.ts", + "import": "./dist/migrations.js", + "default": "./dist/migrations.js" } }, "scripts": { diff --git a/packages/schema/rolldown.config.ts b/packages/schema/rolldown.config.ts index 649f3e2..3807cde 100644 --- a/packages/schema/rolldown.config.ts +++ b/packages/schema/rolldown.config.ts @@ -1,11 +1,10 @@ import { defineConfig } from "rolldown"; export default defineConfig({ - input: "src/index.ts", + input: ["src/index.ts", "src/migrations.ts"], output: { format: "esm", dir: "dist", - entryFileNames: "index.js", }, external: (id: string) => id === "drizzle-orm" || diff --git a/packages/schema/src/constants.ts b/packages/schema/src/constants.ts new file mode 100644 index 0000000..b531b3f --- /dev/null +++ b/packages/schema/src/constants.ts @@ -0,0 +1,8 @@ +export const PROJECT_STATUSES = ["active", "archived", "completed"] as const; +export type ProjectStatus = (typeof PROJECT_STATUSES)[number]; + +export const TASK_STATUSES = ["todo", "in_progress", "in_review", "done"] as const; +export type TaskStatus = (typeof TASK_STATUSES)[number]; + +export const PRIORITIES = ["low", "medium", "high", "urgent"] as const; +export type Priority = (typeof PRIORITIES)[number]; diff --git a/packages/schema/src/drizzle/projects.ts b/packages/schema/src/drizzle/projects.ts index 89173d5..2ddc9dc 100644 --- a/packages/schema/src/drizzle/projects.ts +++ b/packages/schema/src/drizzle/projects.ts @@ -2,9 +2,7 @@ import { pgTable, uuid, text, timestamp, check } from "drizzle-orm/pg-core"; import { sql } from "drizzle-orm"; import { createInsertSchema, createSelectSchema, createUpdateSchema } from "drizzle-valibot"; import { InferOutput, picklist, pipe } from "valibot"; - -export const PROJECT_STATUSES = ["active", "archived", "completed"] as const; -export type ProjectStatus = (typeof PROJECT_STATUSES)[number]; +import { PROJECT_STATUSES } from "../constants"; const projectStatusValues = PROJECT_STATUSES.map((s) => `'${s}'`).join(", "); const createProjectStatusSchema = (schema: any) => pipe(schema, picklist([...PROJECT_STATUSES])) as any; diff --git a/packages/schema/src/drizzle/tasks.ts b/packages/schema/src/drizzle/tasks.ts index 61c7455..2fcff6f 100644 --- a/packages/schema/src/drizzle/tasks.ts +++ b/packages/schema/src/drizzle/tasks.ts @@ -3,12 +3,7 @@ import { sql } from "drizzle-orm"; import { createInsertSchema, createSelectSchema, createUpdateSchema } from "drizzle-valibot"; import { InferOutput, picklist, pipe } from "valibot"; import { projects } from "./projects"; - -export const TASK_STATUSES = ["todo", "in_progress", "in_review", "done"] as const; -export type TaskStatus = (typeof TASK_STATUSES)[number]; - -export const PRIORITIES = ["low", "medium", "high", "urgent"] as const; -export type Priority = (typeof PRIORITIES)[number]; +import { TASK_STATUSES, PRIORITIES } from "../constants"; const taskStatusValues = TASK_STATUSES.map((s) => `'${s}'`).join(", "); const priorityValues = PRIORITIES.map((p) => `'${p}'`).join(", "); diff --git a/packages/schema/src/index.ts b/packages/schema/src/index.ts index 2ad3e67..75e3321 100644 --- a/packages/schema/src/index.ts +++ b/packages/schema/src/index.ts @@ -1,34 +1,30 @@ -import { fileURLToPath } from "url"; -import { dirname, resolve } from "path"; - -const __dirname = dirname(fileURLToPath(import.meta.url)); - -export const migrationsDir = resolve(__dirname, "../drizzle"); +export { + PROJECT_STATUSES, + type ProjectStatus, + TASK_STATUSES, + type TaskStatus, + PRIORITIES, + type Priority, +} from "./constants"; export { eq } from "drizzle-orm"; export { drizzle, NodePgDatabase } from "drizzle-orm/node-postgres"; export { migrate } from "drizzle-orm/node-postgres/migrator"; export { - PROJECT_STATUSES, projects, projectSelect, projectInsert, projectUpdate, type Project, - type ProjectStatus, type ProjectInsertDTO, type ProjectUpdateDTO, } from "./drizzle/projects"; export { projectsRelations, tasksRelations } from "./drizzle/relations"; export { - TASK_STATUSES, - PRIORITIES, tasks, taskSelect, taskInsert, taskUpdate, type Task, - type TaskStatus, - type Priority, } from "./drizzle/tasks"; diff --git a/packages/schema/src/migrations.ts b/packages/schema/src/migrations.ts new file mode 100644 index 0000000..1d1c044 --- /dev/null +++ b/packages/schema/src/migrations.ts @@ -0,0 +1,6 @@ +import { fileURLToPath } from "url"; +import { dirname, resolve } from "path"; + +const __dirname = dirname(fileURLToPath(import.meta.url)); + +export const migrationsDir = resolve(__dirname, "../drizzle");