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
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import { useI18n } from "@/locales/client";
import { Alert, AlertDescription, AlertTitle } from "@bubba/ui/alert";
import { Button } from "@bubba/ui/button";
import { Card, CardContent, CardHeader } from "@bubba/ui/card";
import { Skeleton } from "@bubba/ui/skeleton";
import Bold from "@tiptap/extension-bold";
Expand All @@ -22,13 +21,13 @@ import TableRow from "@tiptap/extension-table-row";
import Text from "@tiptap/extension-text";
import Underline from "@tiptap/extension-underline";
import { EditorContent, useEditor } from "@tiptap/react";
import { AlertCircle, Save } from "lucide-react";
import { AlertCircle } from "lucide-react";
import { redirect } from "next/navigation";
import { EditorRoot } from "novel";
import React, { useEffect, useState } from "react";
import { useDebouncedCallback } from "use-debounce";
import { usePolicyDetails } from "../../hooks/usePolicy";
import { PolicyHeader } from "./PolicyHeader";
import "@bubba/ui/editor.css";
import { useDebouncedCallback } from "use-debounce";

interface PolicyDetailsProps {
policyId: string;
Expand All @@ -45,11 +44,11 @@ export function PolicyDetails({ policyId }: PolicyDetailsProps) {
const [currentContent, setCurrentContent] = useState<any>(null);
const [initialLoadComplete, setInitialLoadComplete] = useState(false);

// Function to save content with debounce
const debouncedSave = useDebouncedCallback(async (content: any) => {
if (!policy) return;

setSaveStatus("Saving");

try {
const contentToSave =
content.type === "doc" && Array.isArray(content.content)
Expand All @@ -66,7 +65,7 @@ export function PolicyDetails({ policyId }: PolicyDetailsProps) {
console.error("Failed to save policy:", err);
setSaveStatus("Unsaved");
}
}, 1000);
}, 1500);

const editor = useEditor({
extensions: [
Expand Down Expand Up @@ -97,7 +96,7 @@ export function PolicyDetails({ policyId }: PolicyDetailsProps) {
editorProps: {
attributes: {
class:
"prose dark:prose-invert focus:outline-none h-full w-full focus:outline-none text-foreground px-16 py-16 max-w-[900px] mx-auto",
"prose dark:prose-invert focus:outline-none h-full w-full focus:outline-none text-foreground max-w-none",
},
},
onUpdate: ({ editor }) => {
Expand Down Expand Up @@ -137,12 +136,6 @@ export function PolicyDetails({ policyId }: PolicyDetailsProps) {
}
}, [editor, policy, initialLoadComplete]);

// Manual save function
const handleManualSave = () => {
if (!editor || !policy) return;
debouncedSave.flush();
};

if (error) {
if (error.code === "NOT_FOUND") {
redirect("/policies");
Expand Down Expand Up @@ -184,25 +177,17 @@ export function PolicyDetails({ policyId }: PolicyDetailsProps) {
);
}

if (!policy) return null;
if (!policy) return redirect("/policies");

return (
<div className="relative max-h-[calc(100vh-100px)] w-full">
<div className="absolute right-5 top-5 z-10 mb-5 gap-2 hidden sm:flex">
<div className="bg-accent/50 px-2 py-1 text-sm text-muted-foreground rounded-md">
{saveStatus}
</div>
<div className="bg-accent/50 px-2 py-1 text-sm text-muted-foreground rounded-md">
{wordCount} Words
</div>
</div>

<EditorRoot>
<EditorContent
className="max-h-[calc(100vh-100px)] prose prose-sm max-w-none overflow-auto"
editor={editor}
/>
</EditorRoot>
<div className="flex flex-col h-full mx-auto">
<PolicyHeader
policy={policy}
saveStatus={saveStatus}
wordCount={wordCount}
status={policy.status}
/>
<EditorContent className="prose prose-sm max-w-none" editor={editor} />
</div>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
"use client";

import { StatusPolicies, type StatusType } from "@/components/status-policies";
import type { PolicyStatus } from "@bubba/db";
import { Badge } from "@bubba/ui/badge";
import { formatDistanceToNow } from "date-fns";
import { Calendar, Clock } from "lucide-react";
import { useState } from "react";
import type { PolicyDetails } from "../types";

interface PolicyHeaderProps {
policy: PolicyDetails;
saveStatus: "Saved" | "Saving" | "Unsaved";
wordCount: number;
status: PolicyStatus;
}

export function PolicyHeader({
policy,
saveStatus,
wordCount,
status,
}: PolicyHeaderProps) {
const [isEditing, setIsEditing] = useState(false);
const [policyName, setPolicyName] = useState(policy.policy.name);

const handleNameChange = (e: React.FocusEvent<HTMLHeadingElement>) => {
setIsEditing(false);
};

const lastUpdated = formatDistanceToNow(new Date(policy.updatedAt), {
addSuffix: true,
});

return (
<div className="border-b border-border bg-background/80 backdrop-blur-sm w-full">
<div className="py-4">
<div className="mx-auto">
<div className="flex flex-col sm:flex-row sm:items-center justify-between">
<div className="flex flex-col">
{isEditing ? (
<h1
className="text-2xl font-semibold outline-none"
contentEditable
suppressContentEditableWarning
onBlur={handleNameChange}
onKeyDown={(e) => {
if (e.key === "Enter") {
e.preventDefault();
e.currentTarget.blur();
}
}}
>
{policyName}
</h1>
) : (
<h1
className="text-2xl font-semibold cursor-text hover:bg-accent/30"
onClick={() => setIsEditing(true)}
>
{policy.policy.name}
</h1>
)}
<div className="flex items-center gap-2 mt-1">
<StatusPolicies
className="text-sm"
status={status as StatusType}
/>
</div>
<div className="flex flex-row items-center text-sm text-muted-foreground mt-1">
<div className="flex items-center gap-1">
<Calendar className="h-3 w-3" />
<span>{lastUpdated}</span>
</div>
</div>
</div>

<div className="flex flex-row items-center gap-2 mt-2 sm:mt-0">
<div className="flex items-center gap-2">
<Badge variant="outline" className="flex items-center gap-1">
<Clock className="h-3 w-3" />
{saveStatus}
</Badge>
<Badge variant="outline" className="flex items-center gap-1">
{wordCount} words
</Badge>
</div>
</div>
</div>
</div>
</div>
</div>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,6 @@ import { setStaticParamsLocale } from "next-international/server";
import { redirect } from "next/navigation";
import { PolicyDetails } from "./components/PolicyDetails";

export const dynamic = "force-dynamic"; // Force dynamic rendering
export const revalidate = 0; // Disable caching

export default async function PolicyDetailsPage({
params,
}: {
Expand All @@ -26,7 +23,6 @@ export default async function PolicyDetailsPage({
return <PolicyDetails policyId={policyId} />;
}

// Add these headers to prevent caching
export async function generateMetadata({
params,
}: {
Expand All @@ -39,11 +35,5 @@ export async function generateMetadata({

return {
title: t("sub_pages.policies.policy_details"),
// Add cache control headers
other: {
"Cache-Control": "no-cache, no-store, must-revalidate",
Pragma: "no-cache",
Expires: "0",
},
};
}
10 changes: 8 additions & 2 deletions apps/app/src/components/status-policies.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,17 @@ const STATUS_COLORS: Record<StatusType, string> = {
needs_review: "#ff0000",
} as const;

export function StatusPolicies({ status }: { status: StatusType }) {
export function StatusPolicies({
status,
className,
}: {
status: StatusType;
className?: string;
}) {
const t = useI18n();

return (
<div className="flex items-center gap-2">
<div className={cn("flex items-center gap-2", className)}>
<div
className={cn("size-2.5")}
style={{ backgroundColor: STATUS_COLORS[status] ?? " " }}
Expand Down
17 changes: 0 additions & 17 deletions apps/web/src/app/(home)/page.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
import { WaitlistForm } from "@/app/components/waitlist-form";
import { Button } from "@bubba/ui/button";
import type { Metadata } from "next";
import Link from "next/link";
import Balancer from "react-wrap-balancer";
import Logo from "../components/logo";

Expand All @@ -27,20 +24,6 @@ export default function Home() {
<div className="mt-10 w-full max-w-md">
<WaitlistForm />
</div>

<div className="mt-12 w-full max-w-[800px]">
<div className="mt-8 flex justify-center">
<Button variant="action">
<Link
href="https://discord.gg/compai"
className="flex items-center gap-2"
>
Join us on Discord
<span>→</span>
</Link>
</Button>
</div>
</div>
</div>
</div>
</section>
Expand Down
22 changes: 12 additions & 10 deletions apps/web/src/app/components/logo-header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,21 @@ import type * as React from "react";

const LogoHeader = (props: React.SVGProps<SVGSVGElement>) => (
<svg
width={227}
height={56}
viewBox="0 0 227 56"
fill="currentColor"
aria-label="Comp AI Logo"
role="img"
width={333.942}
height={70}
viewBox="0 0 333.942 70"
fill="none"
xmlns="http://www.w3.org/2000/svg"
{...props}
>
<path
d="m41 13.333-1.632-1.173-10.783-7.741a1 1 0 0 0-1.166 0L2.417 22.366a1 1 0 0 0-.417.812v9.641a1 1 0 0 0 .417.813l25.002 17.95a1 1 0 0 0 1.166 0l24.998-17.95a1 1 0 0 0 .417-.813v-9.64a1 1 0 0 0-.417-.813zM27.419 9.118a1 1 0 0 1 1.166 0l6.379 4.579a1 1 0 0 1 0 1.625l-3.026 2.17a.64.64 0 0 1-.743-.001l-2.61-1.873a1 1 0 0 0-1.166 0l-8.685 6.235a1 1 0 0 0 0 1.625l2.432 1.745 3.272 2.353 2.98 2.139a1 1 0 0 0 1.167 0l8.685-6.24a1 1 0 0 0 0-1.624l-2.058-1.48a.328.328 0 0 1 0-.532l3.575-2.564a1 1 0 0 1 1.166 0l6.376 4.578a1 1 0 0 1 0 1.625l-3.027 2.173-14.715 10.566a1 1 0 0 1-1.166 0l-7.51-5.392-3.273-2.347-3.935-2.825-3.026-2.173a1 1 0 0 1 0-1.625zM81.75 40.504q-4.536 0-7.728-2.1-3.192-2.142-4.83-5.67-1.596-3.57-1.596-7.854t1.596-7.812q1.638-3.57 4.83-5.67 3.192-2.142 7.728-2.142 3.486 0 6.174 1.344 2.73 1.344 4.41 3.696 1.722 2.352 2.268 5.334H91.83q-.588-3.444-3.36-5.586-2.73-2.142-6.72-2.142-3.696 0-6.258 1.722-2.52 1.722-3.78 4.662t-1.26 6.594 1.26 6.594 3.78 4.662q2.562 1.722 6.258 1.722 2.814 0 5.04-1.092 2.226-1.134 3.57-3.108 1.386-1.974 1.68-4.494h2.772q-.42 3.234-2.142 5.838-1.68 2.562-4.494 4.032-2.772 1.47-6.426 1.47m26.571 0q-3.108 0-5.46-1.47-2.31-1.47-3.612-4.074-1.26-2.604-1.26-5.88t1.26-5.88q1.302-2.604 3.612-4.074 2.352-1.47 5.46-1.47t5.418 1.47q2.352 1.47 3.612 4.074 1.302 2.604 1.302 5.88t-1.302 5.88q-1.26 2.604-3.612 4.074-2.31 1.47-5.418 1.47m0-2.478q3.654 0 5.628-2.436 1.974-2.478 1.974-6.51t-1.974-6.468q-1.974-2.478-5.628-2.478t-5.628 2.478q-1.974 2.436-1.974 6.468t1.974 6.51q1.974 2.436 5.628 2.436m14.176-19.866h2.478v3.822q1.008-2.016 2.814-3.15 1.848-1.176 4.578-1.176 3.192 0 4.956 1.47 1.764 1.428 2.394 3.696.924-2.394 2.814-3.78 1.932-1.386 4.956-1.386 4.158 0 5.922 2.352 1.764 2.31 1.764 5.334V40h-2.604V25.552q0-2.478-1.47-3.948t-4.284-1.47q-2.184 0-4.494 1.512-2.268 1.47-2.268 6.006V40h-2.604V25.552q0-2.478-1.47-3.948t-4.284-1.47q-2.016 0-4.2 1.302t-2.52 5.166V40h-2.478zm40.132 0v4.956q1.806-5.46 8.022-5.46 4.41 0 6.972 3.024t2.562 8.4-2.562 8.4-6.972 3.024q-3.15 0-5.04-1.176-1.89-1.218-2.814-3.528v12.6h-2.646V18.16zm-.042 10.92q0 4.284 1.932 6.594t5.418 2.31q3.612 0 5.544-2.31 1.974-2.31 1.974-6.594t-1.974-6.594q-1.932-2.31-5.544-2.31-3.486 0-5.418 2.31t-1.932 6.594m48.684 1.806h-14.826L193.127 40h-3.108l11.256-30.24h5.124L217.571 40h-2.982zm-.966-2.646-6.468-17.682-6.426 17.682zM221.19 9.76h2.73V40h-2.73z"
fill="currentColor"
/>
<g clipPath="url(#a)" fill="currentColor">
<path d="M117.26 54.456q-4.631 -3.062 -6.98 -8.191 -2.345 -5.124 -2.348 -11.316c-0.003 -6.192 0.781 -7.895 2.348 -11.311q2.345 -5.123 6.98 -8.191 4.631 -3.063 11.213 -3.065 5.06 0 8.992 1.94c3.931 1.94 4.752 3.074 6.401 5.337 1.649 2.267 2.731 4.83 3.261 7.704h-4.022q-0.853 -4.974 -4.844 -8.065c-3.99 -3.091 -5.922 -3.094 -9.782 -3.094q-5.365 -0.001 -9.051 2.488 -3.69 2.491 -5.515 6.731 -1.83 4.243 -1.83 9.526c0 3.521 0.609 6.691 1.83 9.526q1.83 4.246 5.515 6.731 3.688 2.489 9.051 2.488 4.082 0 7.312 -1.61 3.23 -1.604 5.211 -4.459c1.981 -2.855 2.122 -4.063 2.409 -6.49h4.022c-0.409 3.115 -1.431 5.914 -3.08 8.401q-2.472 3.731 -6.523 5.853c-4.05 2.122 -5.821 2.122 -9.355 2.122q-6.586 0 -11.213 -3.066zm40.687 0.942q-3.381 -2.122 -5.242 -5.884 -1.861 -3.759 -1.861 -8.492 0 -4.731 1.861 -8.492c1.861 -3.762 2.989 -4.469 5.242 -5.883q3.38 -2.122 7.892 -2.122 4.514 0 7.892 2.122 3.381 2.122 5.242 5.883c1.861 3.76 1.861 5.337 1.861 8.492s-0.62 5.984 -1.861 8.492 -2.989 4.469 -5.242 5.884 -4.889 2.121 -7.892 2.121 -5.639 -0.707 -7.892 -2.121m16.064 -5.006q2.863 -3.55 2.862 -9.374c-0.001 -5.823 -0.958 -7.007 -2.862 -9.37q-2.863 -3.55 -8.17 -3.55c-5.306 0 -6.261 1.184 -8.17 3.55s-2.862 5.492 -2.862 9.37 0.952 7.007 2.862 9.374 4.633 3.546 8.17 3.546 6.261 -1.184 8.17 -3.546m11.577 -25.145h3.599v5.516q1.467 -2.911 4.113 -4.578 2.653 -1.67 6.612 -1.67 4.631 0 7.192 2.092 2.561 2.093 3.472 5.366 1.338 -3.459 4.113 -5.457 2.776 -2.001 7.161 -2.001 6.034 0 8.593 3.366 2.558 3.363 2.561 7.734V56.784h-3.78V35.92c0 -2.387 -0.711 -4.284 -2.131 -5.704q-2.134 -2.121 -6.22 -2.122 -3.17 0 -6.49 2.152c-2.218 1.435 -3.321 4.339 -3.321 8.702v17.836h-3.78V35.918c0 -2.387 -0.711 -4.284 -2.131 -5.704q-2.134 -2.122 -6.22 -2.122 -2.926 0 -6.093 1.882t-3.661 7.466v19.351h-3.599V25.246zm58.212 0.003v7.158q2.624 -7.885 11.642 -7.885 6.401 -0.001 10.121 4.368t3.718 12.132c0 7.763 -1.239 9.219 -3.718 12.132q-3.718 4.367 -10.121 4.368 -4.571 0 -7.312 -1.73c-2.743 -1.73 -3.191 -2.839 -4.082 -5.067v18.197h-3.84V25.249h3.599zm2.743 25.295q2.804 3.338 7.862 3.336c3.373 0 6.184 -1.114 8.075 -3.336s2.832 -5.397 2.832 -9.526 -0.942 -7.298 -2.832 -9.526q-2.835 -3.335 -8.075 -3.336c-3.493 0 -5.992 1.114 -7.862 3.336s-2.801 5.397 -2.801 9.526 0.932 7.298 2.801 9.526m70.277 -6.91h-21.515l-4.813 13.161h-4.511l16.334 -43.679h7.434l16.215 43.677h-4.329zm-1.401 -3.822 -9.384 -25.542 -9.324 25.542h18.714zM329.981 13.118h3.961v43.677h-3.963zm-271.506 0.522 -2.444 -1.705L39.008 0.076 0.06 27.201v15.504l38.944 27.132 38.938 -27.132V27.201L58.467 13.639zM39.01 6.909l12.121 8.446 -6.224 4.333 -0.554 0.386 -5.338 -3.717 -15.579 10.849 5.338 3.721 4.899 3.416 5.338 3.721 15.574 -10.854 -5.333 -3.717 0.554 -0.386 6.223 -4.333 12.116 8.441 -6.223 4.339 -22.911 15.96 -12.121 -8.446 -4.899 -3.412 -5.897 -4.102 -6.222 -4.339z" />
</g>
<defs>
<clipPath id="a">
<path fill="currentColor" d="M0 0h333.942v69.912H0z" />
</clipPath>
</defs>
</svg>
);

Expand Down
2 changes: 1 addition & 1 deletion packages/ui/src/components/badge.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import type * as React from "react";
import { cn } from "../utils";

const badgeVariants = cva(
"inline-flex items-center rounded-sm border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
"inline-flex items-center rounded-none border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
{
variants: {
variant: {
Expand Down
2 changes: 1 addition & 1 deletion packages/ui/src/editor.css
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ pre {

/* Base editor styles */
.ProseMirror {
@apply w-full px-8 py-6 text-sm leading-normal;
@apply w-full py-6 text-sm leading-normal;
height: 100%;
min-height: 350px;
overflow-y: auto;
Expand Down