Update remaining page headers

Signed-off-by: Émile Ré <nemile.re@gmail.com>
This commit is contained in:
Émile Ré
2025-03-21 11:37:40 +04:00
parent ffa0072b35
commit cee114089a
12 changed files with 300 additions and 258 deletions

View File

@@ -291,6 +291,7 @@ function BreadcrumbControlOverview() {
{data.control?.name} {data.control?.name}
</BreadCrumbNavLink> </BreadCrumbNavLink>
</BreadcrumbItem> </BreadcrumbItem>
<Outlet />
</> </>
); );
} }
@@ -306,6 +307,17 @@ function BreadcrumbCreateControl() {
); );
} }
function BreadcrumbUpdateControl() {
return (
<>
<BreadcrumbSeparator />
<BreadcrumbItem>
<BreadcrumbPage>Update Control</BreadcrumbPage>
</BreadcrumbItem>
</>
);
}
function BreadcrumbPolicyList() { function BreadcrumbPolicyList() {
const { organizationId } = useParams(); const { organizationId } = useParams();
return ( return (
@@ -413,7 +425,12 @@ export default function ConsoleLayout() {
<BreadcrumbControlOverview /> <BreadcrumbControlOverview />
</Suspense> </Suspense>
} }
/> >
<Route
path="update"
element={<BreadcrumbUpdateControl />}
/>
</Route>
<Route <Route
path="update" path="update"
element={<BreadcrumbUpdateFramework />} element={<BreadcrumbUpdateFramework />}

View File

@@ -75,6 +75,7 @@ import type { ControlOverviewPageAssignTaskMutation as ControlOverviewPageAssign
import type { ControlOverviewPageUnassignTaskMutation as ControlOverviewPageUnassignTaskMutationType } from "./__generated__/ControlOverviewPageUnassignTaskMutation.graphql"; import type { ControlOverviewPageUnassignTaskMutation as ControlOverviewPageUnassignTaskMutationType } from "./__generated__/ControlOverviewPageUnassignTaskMutation.graphql";
import type { ControlOverviewPageOrganizationQuery$data } from "./__generated__/ControlOverviewPageOrganizationQuery.graphql"; import type { ControlOverviewPageOrganizationQuery$data } from "./__generated__/ControlOverviewPageOrganizationQuery.graphql";
import type { ControlOverviewPageUpdateControlStateMutation as ControlOverviewPageUpdateControlStateMutationType } from "./__generated__/ControlOverviewPageUpdateControlStateMutation.graphql"; import type { ControlOverviewPageUpdateControlStateMutation as ControlOverviewPageUpdateControlStateMutationType } from "./__generated__/ControlOverviewPageUpdateControlStateMutation.graphql";
import { PageHeader } from "./PageHeader";
// Function to format ISO8601 duration to human-readable format // Function to format ISO8601 duration to human-readable format
const formatDuration = (isoDuration: string): string => { const formatDuration = (isoDuration: string): string => {
@@ -1131,10 +1132,11 @@ function ControlOverviewPageContent({
<Helmet> <Helmet>
<title>{data.control.name || "Control"} - Probo</title> <title>{data.control.name || "Control"} - Probo</title>
</Helmet> </Helmet>
<div className="min-h-screen bg-white p-6 space-y-6"> <div className="container">
<div className="space-y-4 mb-8"> <PageHeader
<div className="flex items-center justify-between"> className="mb-17"
<h1 className="text-2xl font-semibold">{data.control.name}</h1> title={data.control.name ?? ""}
actions={
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<Button variant="outline" size="sm" onClick={handleEditControl}> <Button variant="outline" size="sm" onClick={handleEditControl}>
Edit Control Edit Control
@@ -1167,7 +1169,9 @@ function ControlOverviewPageContent({
{formatImportance(data.control.importance)} {formatImportance(data.control.importance)}
</div> </div>
</div> </div>
</div> }
/>
<div className="space-y-4 mb-8">
<Card className="mt-4"> <Card className="mt-4">
<CardContent className="pt-6"> <CardContent className="pt-6">
<div className="prose prose-gray prose-sm md:prose-base text-gray-600 max-w-3xl"> <div className="prose prose-gray prose-sm md:prose-base text-gray-600 max-w-3xl">

View File

@@ -21,6 +21,7 @@ import {
CreateControlPageCreateControlMutation, CreateControlPageCreateControlMutation,
ControlImportance, ControlImportance,
} from "./__generated__/CreateControlPageCreateControlMutation.graphql"; } from "./__generated__/CreateControlPageCreateControlMutation.graphql";
import { PageHeader } from "./PageHeader";
const createControlMutation = graphql` const createControlMutation = graphql`
mutation CreateControlPageCreateControlMutation( mutation CreateControlPageCreateControlMutation(
@@ -185,13 +186,12 @@ function CreateControlPageContent() {
<Helmet> <Helmet>
<title>Create Control - Probo</title> <title>Create Control - Probo</title>
</Helmet> </Helmet>
<div className="container mx-auto py-6"> <div className="container">
<div className="mb-6"> <PageHeader
<h1 className="text-2xl font-bold">Create Control</h1> className="mb-17"
<p className="text-muted-foreground"> title="Create Control"
Create a new control for your framework description="Create a new control for your framework"
</p> />
</div>
<Card className="max-w-2xl"> <Card className="max-w-2xl">
<form onSubmit={handleSubmit} className="p-6 space-y-6"> <form onSubmit={handleSubmit} className="p-6 space-y-6">

View File

@@ -10,6 +10,7 @@ import { useToast } from "@/hooks/use-toast";
import { HelpCircle } from "lucide-react"; import { HelpCircle } from "lucide-react";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
import { CreatePeoplePageCreatePeopleMutation } from "./__generated__/CreatePeoplePageCreatePeopleMutation.graphql"; import { CreatePeoplePageCreatePeopleMutation } from "./__generated__/CreatePeoplePageCreatePeopleMutation.graphql";
import { PageHeader } from "./PageHeader";
const createPeopleMutation = graphql` const createPeopleMutation = graphql`
mutation CreatePeoplePageCreatePeopleMutation( mutation CreatePeoplePageCreatePeopleMutation(
@@ -153,10 +154,15 @@ function CreatePeoplePageContent() {
<Helmet> <Helmet>
<title>Create Person - Probo Console</title> <title>Create Person - Probo Console</title>
</Helmet> </Helmet>
<div className="container">
<PageHeader
className="mb-17"
title="Add a Person"
description="Add a new person interacting with organization"
/>
<form onSubmit={handleSubmit}> <form onSubmit={handleSubmit}>
<div className="space-y-6 p-4 md:p-6 lg:p-8"> <div className="max-w-4xl space-y-6">
<div className="mx-auto max-w-4xl space-y-6">
<EditableField <EditableField
label="Full Name" label="Full Name"
value={formData.fullName} value={formData.fullName}
@@ -291,19 +297,23 @@ function CreatePeoplePageContent() {
</div> </div>
</Card> </Card>
</div> </div>
</div> <div className="fixed bottom-6 right-6 flex gap-2">
<div className="fixed bottom-6 right-6 flex gap-2"> <Button
<Button type="button" variant="outline" onClick={() => navigate(-1)}> type="button"
Cancel variant="outline"
</Button> onClick={() => navigate(-1)}
<Button >
type="submit" Cancel
className="bg-primary text-primary-foreground hover:bg-primary/90" </Button>
> <Button
Create Person type="submit"
</Button> className="bg-primary text-primary-foreground hover:bg-primary/90"
</div> >
</form> Create Person
</Button>
</div>
</form>
</div>
</> </>
); );
} }

View File

@@ -14,13 +14,14 @@ import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label"; import { Label } from "@/components/ui/label";
import { useToast } from "@/hooks/use-toast"; import { useToast } from "@/hooks/use-toast";
import { FileText, Calendar, User } from "lucide-react"; import { Calendar, User } from "lucide-react";
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"; import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
import PolicyEditor from "@/components/PolicyEditor"; import PolicyEditor from "@/components/PolicyEditor";
import PeopleSelector from "@/components/PeopleSelector"; import PeopleSelector from "@/components/PeopleSelector";
import { Suspense } from "react"; import { Suspense } from "react";
import type { CreatePolicyPageMutation } from "./__generated__/CreatePolicyPageMutation.graphql"; import type { CreatePolicyPageMutation } from "./__generated__/CreatePolicyPageMutation.graphql";
import type { CreatePolicyPageQuery as CreatePolicyPageQueryType } from "./__generated__/CreatePolicyPageQuery.graphql"; import type { CreatePolicyPageQuery as CreatePolicyPageQueryType } from "./__generated__/CreatePolicyPageQuery.graphql";
import { PageHeader } from "./PageHeader";
const CreatePolicyQuery = graphql` const CreatePolicyQuery = graphql`
query CreatePolicyPageQuery($organizationId: ID!) { query CreatePolicyPageQuery($organizationId: ID!) {
@@ -159,20 +160,12 @@ function CreatePolicyForm({
<Helmet> <Helmet>
<title>Create Policy - Probo Console</title> <title>Create Policy - Probo Console</title>
</Helmet> </Helmet>
<div className="container mx-auto py-6"> <div className="container">
<div className="flex items-center mb-6"> <PageHeader
<div className="mr-4"> className="mb-17"
<div className="flex h-12 w-12 items-center justify-center rounded-lg bg-primary/10"> title="Create Policy"
<FileText className="h-6 w-6 text-primary" /> description="Create a new policy for your organization"
</div> />
</div>
<div>
<h1 className="text-2xl font-bold">Create Policy</h1>
<p className="text-muted-foreground">
Create a new policy for your organization
</p>
</div>
</div>
<form onSubmit={handleSubmit}> <form onSubmit={handleSubmit}>
<div className="grid gap-6"> <div className="grid gap-6">

View File

@@ -3,14 +3,16 @@ import { ReactNode } from "react";
export function PageHeader({ export function PageHeader({
className, className,
children,
title, title,
actions, actions,
description, description,
}: { }: {
className?: string; className?: string;
children?: ReactNode;
title: string; title: string;
actions?: ReactNode; actions?: ReactNode;
description: string; description?: string;
}) { }) {
return ( return (
<div className={cn("space-y-6", className)}> <div className={cn("space-y-6", className)}>
@@ -22,7 +24,8 @@ export function PageHeader({
) : ( ) : (
<PageHeading title={title} /> <PageHeading title={title} />
)} )}
<PageDescription>{description}</PageDescription> {description && <PageDescription>{description}</PageDescription>}
{children}
</div> </div>
); );
} }

View File

@@ -18,6 +18,7 @@ import type { PeopleOverviewPageQuery as PeopleOverviewPageQueryType } from "./_
import { useParams } from "react-router"; import { useParams } from "react-router";
import { Helmet } from "react-helmet-async"; import { Helmet } from "react-helmet-async";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
import { PageHeader } from "./PageHeader";
const peopleOverviewPageQuery = graphql` const peopleOverviewPageQuery = graphql`
query PeopleOverviewPageQuery($peopleId: ID!) { query PeopleOverviewPageQuery($peopleId: ID!) {
@@ -161,140 +162,156 @@ function PeopleOverviewPageContent({
return ( return (
<> <>
<div className="space-y-6 p-4 md:p-6 lg:p-8"> <Helmet>
<div className="mx-auto max-w-4xl space-y-6"> <title>Person - Probo</title>
<EditableField </Helmet>
label="Full Name" <div className="container">
value={formData.fullName} <PageHeader className="mb-17" title={formData.fullName} />
onChange={(value) => handleFieldChange("fullName", value)} <div className="space-y-6">
/> <div className="mx-auto max-w-4xl space-y-6">
<EditableField
label="Full Name"
value={formData.fullName}
onChange={(value) => handleFieldChange("fullName", value)}
/>
<EditableField <EditableField
label="Primary Email" label="Primary Email"
value={formData.primaryEmailAddress} value={formData.primaryEmailAddress}
type="email" type="email"
onChange={(value) => onChange={(value) =>
handleFieldChange("primaryEmailAddress", value) handleFieldChange("primaryEmailAddress", value)
} }
/> />
<div className="space-y-2">
<div className="flex items-center gap-2">
<HelpCircle className="h-4 w-4 text-gray-400" />
<Label className="text-sm">Additional Email Addresses</Label>
</div>
<div className="space-y-2"> <div className="space-y-2">
{formData.additionalEmailAddresses.map((email, index) => ( <div className="flex items-center gap-2">
<div key={index} className="flex gap-2"> <HelpCircle className="h-4 w-4 text-gray-400" />
<Input <Label className="text-sm">Additional Email Addresses</Label>
type="email"
value={email}
onChange={(e) => {
const newEmails = [...formData.additionalEmailAddresses];
newEmails[index] = e.target.value;
handleFieldChange("additionalEmailAddresses", newEmails);
}}
/>
<Button
variant="outline"
onClick={() => {
const newEmails =
formData.additionalEmailAddresses.filter(
(_, i) => i !== index
);
handleFieldChange("additionalEmailAddresses", newEmails);
}}
>
Remove
</Button>
</div>
))}
<Button
variant="outline"
onClick={() => {
handleFieldChange("additionalEmailAddresses", [
...formData.additionalEmailAddresses,
"",
]);
}}
>
Add Email
</Button>
</div>
</div>
<Card className="p-6">
<div className="space-y-4">
<div className="space-y-2">
<h2 className="text-lg font-medium">Additional Information</h2>
<p className="text-sm text-gray-500">
Additional details about the person
</p>
</div> </div>
<div className="space-y-2">
{formData.additionalEmailAddresses.map((email, index) => (
<div key={index} className="flex gap-2">
<Input
type="email"
value={email}
onChange={(e) => {
const newEmails = [
...formData.additionalEmailAddresses,
];
newEmails[index] = e.target.value;
handleFieldChange(
"additionalEmailAddresses",
newEmails
);
}}
/>
<Button
variant="outline"
onClick={() => {
const newEmails =
formData.additionalEmailAddresses.filter(
(_, i) => i !== index
);
handleFieldChange(
"additionalEmailAddresses",
newEmails
);
}}
>
Remove
</Button>
</div>
))}
<Button
variant="outline"
onClick={() => {
handleFieldChange("additionalEmailAddresses", [
...formData.additionalEmailAddresses,
"",
]);
}}
>
Add Email
</Button>
</div>
</div>
<Card className="p-6">
<div className="space-y-4"> <div className="space-y-4">
<div className="space-y-2"> <div className="space-y-2">
<div className="flex items-center gap-2"> <h2 className="text-lg font-medium">
<HelpCircle className="h-4 w-4 text-gray-400" /> Additional Information
<Label className="text-sm">Kind</Label> </h2>
</div> <p className="text-sm text-gray-500">
<div className="flex gap-2"> Additional details about the person
<button </p>
onClick={() => handleFieldChange("kind", "EMPLOYEE")} </div>
className={cn(
"rounded-full px-4 py-1 text-sm transition-colors", <div className="space-y-4">
formData.kind === "EMPLOYEE" <div className="space-y-2">
? "bg-blue-100 text-blue-900 ring-2 ring-blue-600 ring-offset-2" <div className="flex items-center gap-2">
: "bg-gray-100 text-gray-900 hover:bg-gray-200" <HelpCircle className="h-4 w-4 text-gray-400" />
)} <Label className="text-sm">Kind</Label>
> </div>
Employee <div className="flex gap-2">
</button> <button
<button onClick={() => handleFieldChange("kind", "EMPLOYEE")}
onClick={() => handleFieldChange("kind", "CONTRACTOR")} className={cn(
className={cn( "rounded-full px-4 py-1 text-sm transition-colors",
"rounded-full px-4 py-1 text-sm transition-colors", formData.kind === "EMPLOYEE"
formData.kind === "CONTRACTOR" ? "bg-blue-100 text-blue-900 ring-2 ring-blue-600 ring-offset-2"
? "bg-purple-100 text-purple-900 ring-2 ring-purple-600 ring-offset-2" : "bg-gray-100 text-gray-900 hover:bg-gray-200"
: "bg-gray-100 text-gray-900 hover:bg-gray-200" )}
)} >
> Employee
Contractor </button>
</button> <button
<button onClick={() => handleFieldChange("kind", "CONTRACTOR")}
onClick={() => className={cn(
handleFieldChange("kind", "SERVICE_ACCOUNT") "rounded-full px-4 py-1 text-sm transition-colors",
} formData.kind === "CONTRACTOR"
className={cn( ? "bg-purple-100 text-purple-900 ring-2 ring-purple-600 ring-offset-2"
"rounded-full px-4 py-1 text-sm transition-colors", : "bg-gray-100 text-gray-900 hover:bg-gray-200"
formData.kind === "SERVICE_ACCOUNT" )}
? "bg-green-100 text-green-900 ring-2 ring-green-600 ring-offset-2" >
: "bg-gray-100 text-gray-900 hover:bg-gray-200" Contractor
)} </button>
> <button
Service Account onClick={() =>
</button> handleFieldChange("kind", "SERVICE_ACCOUNT")
}
className={cn(
"rounded-full px-4 py-1 text-sm transition-colors",
formData.kind === "SERVICE_ACCOUNT"
? "bg-green-100 text-green-900 ring-2 ring-green-600 ring-offset-2"
: "bg-gray-100 text-gray-900 hover:bg-gray-200"
)}
>
Service Account
</button>
</div>
</div> </div>
</div> </div>
</div> </div>
</div> </Card>
</Card> </div>
</div> </div>
</div>
{hasChanges && ( {hasChanges && (
<div className="fixed bottom-6 right-6 flex gap-2"> <div className="fixed bottom-6 right-6 flex gap-2">
<Button variant="outline" onClick={handleCancel}> <Button variant="outline" onClick={handleCancel}>
Cancel Cancel
</Button> </Button>
<Button <Button
onClick={handleSave} onClick={handleSave}
className="bg-primary text-primary-foreground hover:bg-primary/90" className="bg-primary text-primary-foreground hover:bg-primary/90"
> >
Save Changes Save Changes
</Button> </Button>
</div> </div>
)} )}
</div>
</> </>
); );
} }

View File

@@ -18,6 +18,7 @@ import type { PolicyOverviewPageDeleteMutation } from "./__generated__/PolicyOve
import { Helmet } from "react-helmet-async"; import { Helmet } from "react-helmet-async";
import "../styles/policy-content.css"; import "../styles/policy-content.css";
import { useToast } from "@/hooks/use-toast"; import { useToast } from "@/hooks/use-toast";
import { PageHeader } from "./PageHeader";
const PolicyOverviewPageQuery = graphql` const PolicyOverviewPageQuery = graphql`
query PolicyOverviewPageQuery($policyId: ID!) { query PolicyOverviewPageQuery($policyId: ID!) {
@@ -152,72 +153,65 @@ function PolicyOverviewPageContent({
<Helmet> <Helmet>
<title>{policy.name} - Probo Console</title> <title>{policy.name} - Probo Console</title>
</Helmet> </Helmet>
<div className="container mx-auto py-6"> <div className="container">
<div className="flex justify-between items-start mb-6"> <PageHeader
<div className="flex items-center"> className="mb-17"
<div className="mr-4"> title={policy.name ?? ""}
<div className="flex h-12 w-12 items-center justify-center rounded-lg bg-primary/10"> actions={
<FileText className="h-6 w-6 text-primary" /> <div className="flex gap-2">
</div> <Button variant="outline" size="sm" asChild>
</div> <Link
<div> to={`/organizations/${organizationId}/policies/${policy.id}/update`}
<h1 className="text-2xl font-bold">{policy.name}</h1>
<div className="flex items-center gap-2 text-muted-foreground">
<Badge
variant={policy.status === "ACTIVE" ? "default" : "outline"}
> >
{policy.status === "ACTIVE" ? "Active" : "Draft"} <Edit className="h-4 w-4 mr-2" />
</Badge> Edit
{policy.owner && ( </Link>
<div className="flex items-center gap-1"> </Button>
<User className="h-3 w-3" /> <Button
<Link variant="outline"
to={`/organizations/${organizationId}/people/${policy.owner.id}`} size="sm"
className="hover:underline" onClick={() => {
> // Logic to download policy content as PDF or text
{policy.owner.fullName} const element = document.createElement("a");
</Link> const file = new Blob([policy.content || ""], {
</div> type: "text/plain",
)} });
{policy.reviewDate && ( element.href = URL.createObjectURL(file);
<div className="flex items-center gap-1"> element.download = `${policy.name}.txt`;
<Calendar className="h-3 w-3" /> document.body.appendChild(element);
<span>Review: {formatDate(policy.reviewDate)}</span> element.click();
</div> document.body.removeChild(element);
)} }}
</div>
</div>
</div>
<div className="flex gap-2">
<Button variant="outline" size="sm" asChild>
<Link
to={`/organizations/${organizationId}/policies/${policy.id}/update`}
> >
<Edit className="h-4 w-4 mr-2" /> <Download className="h-4 w-4 mr-2" />
Edit Download
</Link> </Button>
</Button> </div>
<Button }
variant="outline" >
size="sm" <div className="flex items-center gap-2 text-muted-foreground">
onClick={() => { <Badge variant={policy.status === "ACTIVE" ? "default" : "outline"}>
// Logic to download policy content as PDF or text {policy.status === "ACTIVE" ? "Active" : "Draft"}
const element = document.createElement("a"); </Badge>
const file = new Blob([policy.content || ""], { {policy.owner && (
type: "text/plain", <div className="flex items-center gap-1">
}); <User className="h-3 w-3" />
element.href = URL.createObjectURL(file); <Link
element.download = `${policy.name}.txt`; to={`/organizations/${organizationId}/people/${policy.owner.id}`}
document.body.appendChild(element); className="hover:underline"
element.click(); >
document.body.removeChild(element); {policy.owner.fullName}
}} </Link>
> </div>
<Download className="h-4 w-4 mr-2" /> )}
Download {policy.reviewDate && (
</Button> <div className="flex items-center gap-1">
<Calendar className="h-3 w-3" />
<span>Review: {formatDate(policy.reviewDate)}</span>
</div>
)}
</div> </div>
</div> </PageHeader>
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6"> <div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
<div className="lg:col-span-2"> <div className="lg:col-span-2">

View File

@@ -28,6 +28,7 @@ import type {
ControlState, ControlState,
ControlImportance, ControlImportance,
} from "./__generated__/UpdateControlPageUpdateControlMutation.graphql"; } from "./__generated__/UpdateControlPageUpdateControlMutation.graphql";
import { PageHeader } from "./PageHeader";
const updateControlMutation = graphql` const updateControlMutation = graphql`
mutation UpdateControlPageUpdateControlMutation($input: UpdateControlInput!) { mutation UpdateControlPageUpdateControlMutation($input: UpdateControlInput!) {
@@ -253,11 +254,12 @@ function UpdateControlPageContent({
<Helmet> <Helmet>
<title>Update Control - Probo</title> <title>Update Control - Probo</title>
</Helmet> </Helmet>
<div className="container mx-auto py-6"> <div className="container">
<div className="mb-6"> <PageHeader
<h1 className="text-2xl font-bold">Update Control</h1> className="mb-17"
<p className="text-muted-foreground">Update the control details</p> title="Update Control"
</div> description="Update the control details"
/>
<Card className="max-w-2xl"> <Card className="max-w-2xl">
<form onSubmit={handleSubmit} className="p-6 space-y-6"> <form onSubmit={handleSubmit} className="p-6 space-y-6">

View File

@@ -18,6 +18,7 @@ import { HelpCircle } from "lucide-react";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
import { UpdateFrameworkPageUpdateFrameworkMutation } from "./__generated__/UpdateFrameworkPageUpdateFrameworkMutation.graphql"; import { UpdateFrameworkPageUpdateFrameworkMutation } from "./__generated__/UpdateFrameworkPageUpdateFrameworkMutation.graphql";
import { UpdateFrameworkPageQuery as UpdateFrameworkPageQueryType } from "./__generated__/UpdateFrameworkPageQuery.graphql"; import { UpdateFrameworkPageQuery as UpdateFrameworkPageQueryType } from "./__generated__/UpdateFrameworkPageQuery.graphql";
import { PageHeader } from "./PageHeader";
const updateFrameworkMutation = graphql` const updateFrameworkMutation = graphql`
mutation UpdateFrameworkPageUpdateFrameworkMutation( mutation UpdateFrameworkPageUpdateFrameworkMutation(
@@ -87,7 +88,7 @@ function EditableField({
} }
className={cn( className={cn(
"w-full resize-none", "w-full resize-none",
required && !value && "border-red-500", required && !value && "border-red-500"
)} )}
placeholder={`Enter ${label.toLowerCase()}`} placeholder={`Enter ${label.toLowerCase()}`}
rows={4} rows={4}
@@ -134,7 +135,7 @@ function UpdateFrameworkPageContent({
const [commit, isInFlight] = const [commit, isInFlight] =
useMutation<UpdateFrameworkPageUpdateFrameworkMutation>( useMutation<UpdateFrameworkPageUpdateFrameworkMutation>(
updateFrameworkMutation, updateFrameworkMutation
); );
const handleFieldChange = (field: keyof typeof formData, value: unknown) => { const handleFieldChange = (field: keyof typeof formData, value: unknown) => {
@@ -202,11 +203,12 @@ function UpdateFrameworkPageContent({
<Helmet> <Helmet>
<title>Update Framework - Probo</title> <title>Update Framework - Probo</title>
</Helmet> </Helmet>
<div className="container mx-auto py-6"> <div className="container">
<div className="mb-6"> <PageHeader
<h1 className="text-2xl font-bold">Update Framework</h1> className="mb-17"
<p className="text-muted-foreground">Update the framework details</p> title="Update Framework"
</div> description="Update the framework details"
/>
<Card className="max-w-2xl"> <Card className="max-w-2xl">
<form onSubmit={handleSubmit} className="p-6 space-y-6"> <form onSubmit={handleSubmit} className="p-6 space-y-6">

View File

@@ -13,13 +13,14 @@ import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label"; import { Label } from "@/components/ui/label";
import { toast } from "@/hooks/use-toast"; import { toast } from "@/hooks/use-toast";
import { FileText, Calendar, User } from "lucide-react"; import { Calendar, User } from "lucide-react";
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"; import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
import { Suspense } from "react"; import { Suspense } from "react";
import PolicyEditor from "@/components/PolicyEditor"; import PolicyEditor from "@/components/PolicyEditor";
import PeopleSelector from "@/components/PeopleSelector"; import PeopleSelector from "@/components/PeopleSelector";
import type { UpdatePolicyPageQuery as UpdatePolicyPageQueryType } from "./__generated__/UpdatePolicyPageQuery.graphql"; import type { UpdatePolicyPageQuery as UpdatePolicyPageQueryType } from "./__generated__/UpdatePolicyPageQuery.graphql";
import type { UpdatePolicyPageMutation as UpdatePolicyPageMutationType } from "./__generated__/UpdatePolicyPageMutation.graphql"; import type { UpdatePolicyPageMutation as UpdatePolicyPageMutationType } from "./__generated__/UpdatePolicyPageMutation.graphql";
import { PageHeader } from "./PageHeader";
const UpdatePolicyPageQuery = graphql` const UpdatePolicyPageQuery = graphql`
query UpdatePolicyPageQuery($policyId: ID!, $organizationId: ID!) { query UpdatePolicyPageQuery($policyId: ID!, $organizationId: ID!) {
@@ -151,18 +152,12 @@ function UpdatePolicyPageContent({
<Helmet> <Helmet>
<title>Update Policy - Probo Console</title> <title>Update Policy - Probo Console</title>
</Helmet> </Helmet>
<div className="container mx-auto py-6"> <div className="container">
<div className="flex items-center mb-6"> <PageHeader
<div className="mr-4"> className="mb-17"
<div className="flex h-12 w-12 items-center justify-center rounded-lg bg-primary/10"> title="Update Policy"
<FileText className="h-6 w-6 text-primary" /> description="Update an existing policy"
</div> />
</div>
<div>
<h1 className="text-2xl font-bold">Update Policy</h1>
<p className="text-muted-foreground">Update an existing policy</p>
</div>
</div>
<form onSubmit={handleSubmit}> <form onSubmit={handleSubmit}>
<div className="grid gap-6"> <div className="grid gap-6">

View File

@@ -18,6 +18,7 @@ import type { VendorOverviewPageQuery as VendorOverviewPageQueryType } from "./_
import { useParams } from "react-router"; import { useParams } from "react-router";
import { Helmet } from "react-helmet-async"; import { Helmet } from "react-helmet-async";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
import { PageHeader } from "./PageHeader";
const vendorOverviewPageQuery = graphql` const vendorOverviewPageQuery = graphql`
query VendorOverviewPageQuery($vendorId: ID!) { query VendorOverviewPageQuery($vendorId: ID!) {
@@ -205,8 +206,12 @@ function VendorOverviewPageContent({
return ( return (
<> <>
<div className="space-y-6 p-4 md:p-6 lg:p-8"> <Helmet>
<div className="mx-auto max-w-4xl space-y-6"> <title>Vendor - Probo</title>
</Helmet>
<div className="container">
<PageHeader className="mb-17" title={formData.name} />
<div className="max-w-4xl space-y-6">
<EditableField <EditableField
label="Name" label="Name"
value={formData.name} value={formData.name}