Skip to content
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
71 changes: 71 additions & 0 deletions prisma/migrations/20260404014942_seperate_tags/migration.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/*
Warnings:

- You are about to drop the `Tag` table. If the table is not empty, all the data it contains will be lost.

*/
-- DropForeignKey
ALTER TABLE "public"."Tag" DROP CONSTRAINT "Tag_orgId_fkey";

-- DropForeignKey
ALTER TABLE "public"."_PositionTags" DROP CONSTRAINT "_PositionTags_B_fkey";

-- DropForeignKey
ALTER TABLE "public"."_TaskTemplateTags" DROP CONSTRAINT "_TaskTemplateTags_A_fkey";

-- DropForeignKey
ALTER TABLE "public"."_TaskTemplateTags" DROP CONSTRAINT "_TaskTemplateTags_B_fkey";

-- DropTable
DROP TABLE "public"."Tag";

-- CreateTable
CREATE TABLE "public"."PositionTag" (
"id" TEXT NOT NULL,
"name" TEXT NOT NULL,
"orgId" TEXT NOT NULL,
"colorHexCode" TEXT NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,

CONSTRAINT "PositionTag_pkey" PRIMARY KEY ("id")
);

-- CreateTable
CREATE TABLE "public"."TaskTemplateTag" (
"id" TEXT NOT NULL,
"name" TEXT NOT NULL,
"orgId" TEXT NOT NULL,
"colorHexCode" TEXT NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,

CONSTRAINT "TaskTemplateTag_pkey" PRIMARY KEY ("id")
);

-- CreateIndex
CREATE INDEX "PositionTag_orgId_idx" ON "public"."PositionTag"("orgId");

-- CreateIndex
CREATE UNIQUE INDEX "PositionTag_name_orgId_key" ON "public"."PositionTag"("name", "orgId");

-- CreateIndex
CREATE INDEX "TaskTemplateTag_orgId_idx" ON "public"."TaskTemplateTag"("orgId");

-- CreateIndex
CREATE UNIQUE INDEX "TaskTemplateTag_name_orgId_key" ON "public"."TaskTemplateTag"("name", "orgId");

-- AddForeignKey
ALTER TABLE "public"."PositionTag" ADD CONSTRAINT "PositionTag_orgId_fkey" FOREIGN KEY ("orgId") REFERENCES "public"."Organization"("id") ON DELETE CASCADE ON UPDATE CASCADE;

-- AddForeignKey
ALTER TABLE "public"."TaskTemplateTag" ADD CONSTRAINT "TaskTemplateTag_orgId_fkey" FOREIGN KEY ("orgId") REFERENCES "public"."Organization"("id") ON DELETE CASCADE ON UPDATE CASCADE;

-- AddForeignKey
ALTER TABLE "public"."_PositionTags" ADD CONSTRAINT "_PositionTags_B_fkey" FOREIGN KEY ("B") REFERENCES "public"."PositionTag"("id") ON DELETE CASCADE ON UPDATE CASCADE;

-- AddForeignKey
ALTER TABLE "public"."_TaskTemplateTags" ADD CONSTRAINT "_TaskTemplateTags_A_fkey" FOREIGN KEY ("A") REFERENCES "public"."TaskTemplate"("id") ON DELETE CASCADE ON UPDATE CASCADE;

-- AddForeignKey
ALTER TABLE "public"."_TaskTemplateTags" ADD CONSTRAINT "_TaskTemplateTags_B_fkey" FOREIGN KEY ("B") REFERENCES "public"."TaskTemplateTag"("id") ON DELETE CASCADE ON UPDATE CASCADE;
30 changes: 22 additions & 8 deletions prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ model Organization {
taskTemplates TaskTemplate[]
members Member[]
invitations Invitation[]
tags Tag[]
positionTags PositionTag[]
taskTemplateTags TaskTemplateTag[]
}

// An internal user of the Sarge platform (e.g. recruiter, reviewer, admin). Users authenticate and may belong to one or more organizations.
Expand Down Expand Up @@ -64,7 +65,7 @@ model Position {
applications Application[]
organization Organization @relation(fields: [orgId], references: [id])
creator User @relation("CreatedPositions", fields: [createdById], references: [id])
tags Tag[] @relation("PositionTags")
tags PositionTag[] @relation("PositionTags")
assessmentTemplate AssessmentTemplate? @relation("derivedFromPosition", fields: [assessmentId], references: [id])
}

Expand Down Expand Up @@ -161,7 +162,7 @@ model TaskTemplate {
organization Organization @relation(fields: [orgId], references: [id])
author User @relation("AuthoredTaskTemplates", fields: [authorId], references: [id], onDelete: Restrict)
tasks Task[] @relation("derivedFromTaskTemplate")
tags Tag[] @relation("TaskTemplateTags")
tags TaskTemplateTag[] @relation("TaskTemplateTags")
languages TaskTemplateLanguage[]

assessments AssessmentTemplateTask[]
Expand Down Expand Up @@ -359,20 +360,33 @@ model Invitation {
@@index([organizationId])
}

// A label used to categorize and organize task templates within an organization.
model Tag {
id String @id @unique @default(cuid())
// A label used to categorize and organize positions within an organization.
model PositionTag {
id String @id @default(cuid())
name String
orgId String
colorHexCode String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt

organization Organization @relation(fields: [orgId], references: [id], onDelete: Cascade)
positions Position[] @relation("PositionTags")

@@unique([name, orgId])
@@index([orgId])
}

// A label used to categorize and organize task templates within an organization.
model TaskTemplateTag {
id String @id @default(cuid())
name String
orgId String
colorHexCode String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt

// These are taggable items.
organization Organization @relation(fields: [orgId], references: [id], onDelete: Cascade)
taskTemplates TaskTemplate[] @relation("TaskTemplateTags")
positions Position[] @relation("PositionTags")

@@unique([name, orgId])
@@index([orgId])
Expand Down
20 changes: 20 additions & 0 deletions prisma/seed-data/position-tags.seed.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
export const positionTagsData = [
{
id: 'position_tag_backend_001',
name: 'Backend',
colorHexCode: '#d6d7fe',
orgId: 'org_nextlab_001',
},
{
id: 'position_tag_fullstack_001',
name: 'Full Stack',
colorHexCode: '#d8f4d4',
orgId: 'org_nextlab_001',
},
{
id: 'position_tag_internship_001',
name: 'Internship',
colorHexCode: '#ffe9bb',
orgId: 'org_nextlab_001',
},
];
Original file line number Diff line number Diff line change
@@ -1,30 +1,30 @@
export const tagsData = [
export const taskTemplateTagsData = [
{
id: 'tag_algorithm_001',
id: 'task_template_tag_algorithm_001',
name: 'Algorithm',
colorHexCode: '#d6d7fe',
orgId: 'org_nextlab_001',
},
{
id: 'tag_data_structure_001',
id: 'task_template_tag_data_structure_001',
name: 'Data Structure',
colorHexCode: '#ededff',
orgId: 'org_nextlab_001',
},
{
id: 'tag_math_001',
id: 'task_template_tag_math_001',
name: 'Math',
colorHexCode: '#ffe9bb',
orgId: 'org_nextlab_001',
},
{
id: 'tag_advanced_001',
id: 'task_template_tag_advanced_001',
name: 'Advanced',
colorHexCode: '#f7c4c4',
orgId: 'org_nextlab_001',
},
{
id: 'tag_loops_001',
id: 'task_template_tag_loops_001',
name: 'Loops',
colorHexCode: '#d8f4d4',
orgId: 'org_nextlab_001',
Expand Down
46 changes: 33 additions & 13 deletions prisma/seed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ import { candidatesData } from './seed-data/candidates.seed';
import { taskTemplatesData } from './seed-data/task-template.seed';
import { assessmentTemplatesData } from './seed-data/assessment-template.seed';
import { assessmentsData } from './seed-data/assessment.seed';
import { tagsData } from './seed-data/tags.seed';
import { positionTagsData } from './seed-data/position-tags.seed';
import { taskTemplateTagsData } from './seed-data/task-template-tags.seed';
import { languageData } from './seed-data/languages.seed';

/**
Expand Down Expand Up @@ -249,21 +250,21 @@ async function seedLanguages() {
async function seedTags() {
console.log('Seeding tags...');

for (const tag of tagsData) {
await prisma.tag.upsert({
for (const tag of taskTemplateTagsData) {
await prisma.taskTemplateTag.upsert({
where: { id: tag.id },
update: {},
create: tag,
});

console.log(` Created tag: ${tag.name}`);
console.log(` Created task template tag: ${tag.name}`);
}

await prisma.taskTemplate.update({
where: { id: taskTemplatesData[0].id },
data: {
tags: {
connect: tagsData.map((tag) => ({ id: tag.id })),
connect: taskTemplateTagsData.map((tag) => ({ id: tag.id })),
},
},
});
Expand All @@ -272,7 +273,7 @@ async function seedTags() {
where: { id: taskTemplatesData[1].id },
data: {
tags: {
connect: [{ id: tagsData[0].id }, { id: tagsData[1].id }],
connect: [{ id: taskTemplateTagsData[0].id }, { id: taskTemplateTagsData[1].id }],
},
},
});
Expand All @@ -281,7 +282,7 @@ async function seedTags() {
where: { id: taskTemplatesData[2].id },
data: {
tags: {
connect: [{ id: tagsData[2].id }, { id: tagsData[0].id }],
connect: [{ id: taskTemplateTagsData[2].id }, { id: taskTemplateTagsData[0].id }],
},
},
});
Expand All @@ -290,7 +291,7 @@ async function seedTags() {
where: { id: taskTemplatesData[3].id },
data: {
tags: {
connect: [{ id: tagsData[3].id }, { id: tagsData[4].id }],
connect: [{ id: taskTemplateTagsData[3].id }, { id: taskTemplateTagsData[4].id }],
},
},
});
Expand All @@ -299,7 +300,7 @@ async function seedTags() {
where: { id: taskTemplatesData[5].id },
data: {
tags: {
connect: [{ id: tagsData[0].id }, { id: tagsData[3].id }],
connect: [{ id: taskTemplateTagsData[0].id }, { id: taskTemplateTagsData[3].id }],
},
},
});
Expand All @@ -308,7 +309,7 @@ async function seedTags() {
where: { id: taskTemplatesData[6].id },
data: {
tags: {
connect: [{ id: tagsData[0].id }, { id: tagsData[1].id }],
connect: [{ id: taskTemplateTagsData[0].id }, { id: taskTemplateTagsData[1].id }],
},
},
});
Expand All @@ -317,7 +318,7 @@ async function seedTags() {
where: { id: taskTemplatesData[7].id },
data: {
tags: {
connect: [{ id: tagsData[1].id }, { id: tagsData[0].id }],
connect: [{ id: taskTemplateTagsData[1].id }, { id: taskTemplateTagsData[0].id }],
},
},
});
Expand All @@ -326,7 +327,7 @@ async function seedTags() {
where: { id: taskTemplatesData[8].id },
data: {
tags: {
connect: [{ id: tagsData[1].id }, { id: tagsData[0].id }],
connect: [{ id: taskTemplateTagsData[1].id }, { id: taskTemplateTagsData[0].id }],
},
},
});
Expand All @@ -335,7 +336,26 @@ async function seedTags() {
where: { id: taskTemplatesData[9].id },
data: {
tags: {
connect: [{ id: tagsData[0].id }, { id: tagsData[1].id }],
connect: [{ id: taskTemplateTagsData[0].id }, { id: taskTemplateTagsData[1].id }],
},
},
});

for (const tag of positionTagsData) {
await prisma.positionTag.upsert({
where: { id: tag.id },
update: {},
create: tag,
});

console.log(` Created position tag: ${tag.name}`);
}

await prisma.position.update({
where: { id: positionsData[0].id },
data: {
tags: {
connect: [{ id: positionTagsData[0].id }, { id: positionTagsData[1].id }],
},
},
});
Expand Down
4 changes: 2 additions & 2 deletions prisma/teardown.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,15 @@ async function main() {
"Application",
"Candidate",
"Position",
"Tag",
"PositionTag",
"TaskTemplateTag",
"Member",
"Invitation",
"Session",
"Account",
"Verification",
"User",
"Organization",
"_TaskTemplateTags"
RESTART IDENTITY CASCADE;
`;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { getSession } from '@/lib/utils/auth.utils';
export async function GET() {
try {
const session = await getSession();
const tags = await TagService.getTagsByOrgId(session.activeOrganizationId);
const tags = await TagService.getPositionTagsByOrgId(session.activeOrganizationId);
return Response.json({ data: tags }, { status: 200 });
} catch (err) {
return handleError(err);
Expand All @@ -20,7 +20,7 @@ export async function POST(request: NextRequest) {
const body = await request.json();
const parsed = createTagSchema.parse(body);

const tag = await TagService.createTag(
const tag = await TagService.createPositionTag(
parsed.name,
session.activeOrganizationId,
parsed.colorHexCode
Expand Down
32 changes: 32 additions & 0 deletions src/app/api/tags/task-template/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { type NextRequest } from 'next/server';
import { handleError } from '@/lib/utils/errors.utils';
import TagService from '@/lib/services/tag.service';
import { createTagSchema } from '@/lib/schemas/tag.schema';
import { getSession } from '@/lib/utils/auth.utils';

export async function GET() {
try {
const session = await getSession();
const tags = await TagService.getTaskTemplateTagsByOrgId(session.activeOrganizationId);
return Response.json({ data: tags }, { status: 200 });
} catch (err) {
return handleError(err);
}
}

export async function POST(request: NextRequest) {
try {
const session = await getSession();
const body = await request.json();
const parsed = createTagSchema.parse(body);

const tag = await TagService.createTaskTemplateTag(
parsed.name,
session.activeOrganizationId,
parsed.colorHexCode
);
return Response.json({ data: tag }, { status: 201 });
} catch (err) {
return handleError(err);
}
}
Loading
Loading