diff --git a/.github/workflows/database-migrations.yml b/.github/workflows/database-migrations.yml new file mode 100644 index 0000000000..e43b13ab58 --- /dev/null +++ b/.github/workflows/database-migrations.yml @@ -0,0 +1,43 @@ +name: Database Migrations + +on: + push: + branches: + - main + paths: + - "packages/db/prisma/schema.prisma" + - "packages/db/prisma/migrations/**" + workflow_dispatch: # Allows manual triggering + +env: + NODE_VERSION: "20.x" + +jobs: + migrate: + name: Run Database Migrations + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: ${{ env.NODE_VERSION }} + cache: "npm" + + - name: Install dependencies + run: npm ci + + - name: Apply database migrations + env: + DATABASE_URL: ${{ secrets.DATABASE_URL_PROD }} + run: | + cd packages/db + npx prisma migrate deploy + + - name: Generate Prisma Client + run: | + cd packages/db + npx prisma generate diff --git a/apps/app/src/app/[locale]/(app)/(dashboard)/policies/[id]/Actions/publish-policy.ts b/apps/app/src/app/[locale]/(app)/(dashboard)/policies/[id]/Actions/publish-policy.ts new file mode 100644 index 0000000000..246cd11317 --- /dev/null +++ b/apps/app/src/app/[locale]/(app)/(dashboard)/policies/[id]/Actions/publish-policy.ts @@ -0,0 +1,59 @@ +"use server"; + +import { authActionClient } from "@/actions/safe-action"; +import { db, type OrganizationPolicy } from "@bubba/db"; +import { z } from "zod"; + +const schema = z.object({ + id: z.string(), +}); + +export type PublishPolicyResponse = { + success: boolean; + data?: OrganizationPolicy; + error?: string; +}; + +export const publishPolicy = authActionClient + .schema(schema) + .metadata({ + name: "publish-policy", + track: { + event: "publish-policy", + channel: "server", + }, + }) + .action(async ({ ctx, parsedInput }) => { + const { user } = ctx; + const { id } = parsedInput; + + if (!user.organizationId) { + return { + success: false, + error: "Not authorized - no organization found", + }; + } + + try { + const policy = await db.organizationPolicy.update({ + where: { + id, + organizationId: user.organizationId!, + }, + data: { + status: "published", + updatedAt: new Date(), + }, + }); + + return { + success: true, + data: policy, + }; + } catch (error) { + return { + success: false, + error: "Failed to publish policy", + }; + } + }); diff --git a/apps/app/src/app/[locale]/(app)/(dashboard)/policies/[id]/Components/PolicyOverview.tsx b/apps/app/src/app/[locale]/(app)/(dashboard)/policies/[id]/Components/PolicyOverview.tsx new file mode 100644 index 0000000000..2d15cd0097 --- /dev/null +++ b/apps/app/src/app/[locale]/(app)/(dashboard)/policies/[id]/Components/PolicyOverview.tsx @@ -0,0 +1,29 @@ +"use client"; + +import type { JSONContent } from "@tiptap/react"; +import PolicyEditor from "@/components/editor/advanced-editor"; +import { usePolicy } from "@/app/[locale]/(app)/(dashboard)/policies/hooks/usePolicy"; +import { Button } from "@bubba/ui/button"; +import { Separator } from "@bubba/ui/separator"; + +export function PolicyOverview({ policyId }: { policyId: string }) { + const { data: policy } = usePolicy({ policyId }); + + if (!policy) return null; + + const content = policy.content as JSONContent; + + if (!content) return null; + + return ( +