diff --git a/src/components/shared/CustomButton.tsx b/src/components/shared/CustomButton.tsx new file mode 100644 index 0000000..55b85ae --- /dev/null +++ b/src/components/shared/CustomButton.tsx @@ -0,0 +1,119 @@ +import type { ReactElement, ButtonHTMLAttributes } from 'react'; +import { cva, type VariantProps } from 'class-variance-authority'; +import { cn } from '@/lib/utils'; +import { useAppTheme } from '@/hooks/useAppTheme'; +import { Loader2 } from 'lucide-react'; + +const customButtonVariants = cva( + 'inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-xs font-medium transition-all disabled:opacity-50 disabled:cursor-not-allowed cursor-pointer outline-none active:scale-[0.98]', + { + variants: { + variant: { + primary: '', // Handled by style prop for theme colors + secondary: '', // Handled by style prop for theme colors + success: 'bg-emerald-600 text-white hover:bg-emerald-700', + danger: 'bg-red-600 text-white hover:bg-red-700', + warning: 'bg-amber-500 text-white hover:bg-amber-600', + outline: 'border border-gray-200 bg-white text-gray-700 hover:bg-gray-50', + ghost: 'bg-transparent text-gray-600 hover:bg-gray-100', + link: 'bg-transparent text-primary underline-offset-4 hover:underline p-0 h-auto', + }, + size: { + xs: 'h-7 px-2 text-[10px]', + sm: 'h-8 px-3', + md: 'h-9 px-4', + lg: 'h-10 px-6 text-sm', + xl: 'h-12 px-8 text-base', + icon: 'h-9 w-9 p-0', + }, + fullWidth: { + true: 'w-full', + } + }, + defaultVariants: { + variant: 'primary', + size: 'md', + }, + } +); + +interface CustomButtonProps + extends ButtonHTMLAttributes, + VariantProps { + isLoading?: boolean; + leftIcon?: React.ReactNode; + rightIcon?: React.ReactNode; +} + +export const CustomButton = ({ + children, + variant, + size, + fullWidth, + className, + disabled, + isLoading, + leftIcon, + rightIcon, + style, + ...props +}: CustomButtonProps): ReactElement => { + const { primaryColor, secondaryColor } = useAppTheme(); + + + const getThemeStyles = () => { + if (variant === 'secondary') { + return { + backgroundColor: secondaryColor, + color: primaryColor, + border: `1px solid ${primaryColor}20`, + }; + } + if (variant === 'primary' || !variant) { + return { + backgroundColor: primaryColor, + color: secondaryColor, + }; + } + return {}; + }; + + return ( + + ); +}; diff --git a/src/components/shared/FileShareModal.tsx b/src/components/shared/FileShareModal.tsx index b40b517..ff3fbb0 100644 --- a/src/components/shared/FileShareModal.tsx +++ b/src/components/shared/FileShareModal.tsx @@ -11,7 +11,8 @@ import { ExternalLink, } from "lucide-react"; import { cn } from "@/lib/utils"; -import { Modal } from "./Modal"; +import { Modal, CustomButton } from "./"; +import { useAppTheme } from "@/hooks/useAppTheme"; import fileAttachmentService, { type FileAttachment } from "@/services/file-attachment-service"; import { DeleteConfirmationModal } from "./DeleteConfirmationModal"; @@ -28,7 +29,8 @@ export const FileShareModal: React.FC = ({ }) => { const [expiresInHours, setExpiresInHours] = useState(24); const [maxDownloads, setMaxDownloads] = useState(""); - const [permissions, setPermissions] = useState<"view" | "download">("download"); + const permissions = "download"; + const { primaryColor } = useAppTheme(); const [isSharing, setIsSharing] = useState(false); const [shareData, setShareData] = useState<{ url: string; token: string; id: string } | null>(null); @@ -124,13 +126,18 @@ export const FileShareModal: React.FC = ({ {[1, 24, 72, 168].map((h) => ( @@ -150,7 +157,11 @@ export const FileShareModal: React.FC = ({ value={maxDownloads} onChange={(e) => setMaxDownloads(e.target.value === "" ? "" : Number(e.target.value))} placeholder="Unlimited" - className="w-full h-10 border border-[rgba(0,0,0,0.12)] rounded-lg px-3 text-sm focus:outline-none focus:ring-1 focus:ring-[#084cc8] focus:border-[#084cc8]" + className="w-full h-10 border border-[rgba(0,0,0,0.12)] rounded-lg px-3 text-sm focus:outline-none focus:ring-1 transition-all" + style={{ + '--tw-ring-color': primaryColor, + borderColor: 'rgba(0,0,0,0.12)' + } as React.CSSProperties} /> @@ -160,34 +171,25 @@ export const FileShareModal: React.FC = ({ Permission - +
+ + Download +
- + Generate Secure Link + ) : (
@@ -208,16 +210,20 @@ export const FileShareModal: React.FC = ({
- + {shareData.url}
@@ -226,19 +232,21 @@ export const FileShareModal: React.FC = ({
- - +
- - + {onAddSub && ( + + )} + {onEdit && ( + + )} {onDelete && (

- Edit Document Metadata + Edit Document

Updates will apply to the current version of the document.

- + */}
diff --git a/src/pages/tenant/ViewDocument.tsx b/src/pages/tenant/ViewDocument.tsx index d508cde..986c732 100644 --- a/src/pages/tenant/ViewDocument.tsx +++ b/src/pages/tenant/ViewDocument.tsx @@ -6,8 +6,9 @@ import { FormSelect, Modal, PrimaryButton, - RichTextEditor, SecondaryButton, + CustomButton, + RichTextEditor, type Column, } from "@/components/shared"; import { @@ -502,76 +503,65 @@ const ViewDocument = (): ReactElement => {
{document?.status === "draft" && (
- - +
)} {document?.status === "in_review" && ( - <> - - - - + +
)} {document?.status === "approved" && (
- - +
)} {document?.status === "effective" && ( - + )}
), @@ -597,9 +587,12 @@ const ViewDocument = (): ReactElement => { {document?.document_type || "-"} - v{document?.current_version || "-"} @@ -616,9 +609,12 @@ const ViewDocument = (): ReactElement => { @@ -881,15 +922,24 @@ const ViewDocument = (): ReactElement => {
- {isMajorVersion ? "Major" : "Minor"} Increment + {isMajorVersion ? "Major" : "Minor"}{" "} + Increment {`${document?.current_version || "0.0"} to ${(() => { - if (!document?.current_version) return isMajorVersion ? "1.0" : "0.1"; - const parts = document.current_version.replace("v", "").split("."); + if (!document?.current_version) + return isMajorVersion ? "1.0" : "0.1"; + const parts = document.current_version + .replace("v", "") + .split("."); let major = parseInt(parts[0]) || 0; let minor = parseInt(parts[1]) || 0; - if (isMajorVersion) { major++; minor = 0; } else { minor++; } + if (isMajorVersion) { + major++; + minor = 0; + } else { + minor++; + } return `${major}.${minor}`; })()}`} @@ -897,8 +947,7 @@ const ViewDocument = (): ReactElement => {

{isMajorVersion ? "Incrementing to a major version indicates significant changes or a full document overhaul." - : `Turn on major versioning to increment to v${(parseInt((document?.current_version || "0.0").split(".")[0]) || 0) + 1}.0 instead of a minor revision.` - } + : `Turn on major versioning to increment to v${(parseInt((document?.current_version || "0.0").split(".")[0]) || 0) + 1}.0 instead of a minor revision.`}

@@ -917,7 +966,6 @@ const ViewDocument = (): ReactElement => { className="w-full px-3 py-2 border border-[rgba(0,0,0,0.08)] rounded-lg text-sm focus:ring-1 focus:ring-[#112868]/20 focus:outline-none transition-all" /> -
@@ -931,7 +979,9 @@ const ViewDocument = (): ReactElement => { disabled={isVersionSaving} className="px-8" > - {isVersionSaving ? "Creating..." : "Save New Version"} + {isVersionSaving + ? "Creating..." + : "Save New Version"}
@@ -942,8 +992,12 @@ const ViewDocument = (): ReactElement => { {/* Version Context Card */}
-

Version Context

-

Reference information for current revision.

+

+ Version Context +

+

+ Reference information for current revision. +

@@ -952,24 +1006,56 @@ const ViewDocument = (): ReactElement => {
{[ - { label: "Document Number", value: document?.document_number }, - { label: "Current Version", value: `v${document?.current_version || "-"}` }, - { label: "Next Version", value: (() => { - if (!document?.current_version) return isMajorVersion ? "v1.0" : "v0.1"; - const parts = document.current_version.replace("v", "").split("."); + { + label: "Document Number", + value: document?.document_number, + }, + { + label: "Current Version", + value: `v${document?.current_version || "-"}`, + }, + { + label: "Next Version", + value: (() => { + if (!document?.current_version) + return isMajorVersion ? "v1.0" : "v0.1"; + const parts = document.current_version + .replace("v", "") + .split("."); let major = parseInt(parts[0]) || 0; let minor = parseInt(parts[1]) || 0; - if (isMajorVersion) { major++; minor = 0; } else { minor++; } + if (isMajorVersion) { + major++; + minor = 0; + } else { + minor++; + } return `v${major}.${minor}`; - })() + })(), + }, + { + label: "Document Type", + value: document?.document_type, + }, + { + label: "Owner", + value: document?.owner?.name || "-", + }, + { + label: "Department", + value: document?.department || "-", }, - { label: "Document Type", value: document?.document_type }, - { label: "Owner", value: document?.owner?.name || "-" }, - { label: "Department", value: document?.department || "-" }, ].map((item, i) => ( -
- {item.label} - {item.value} +
+ + {item.label} + + + {item.value} +
))}
@@ -979,9 +1065,12 @@ const ViewDocument = (): ReactElement => { {/* Review Notes Card */}
-

Review Notes

+

+ Review Notes +

- Creating a new draft revision will automatically trigger the following system actions: + Creating a new draft revision will automatically + trigger the following system actions:

@@ -989,11 +1078,16 @@ const ViewDocument = (): ReactElement => { {[ "Status resets to Draft", "Workflow restarts", - "Version history retained" + "Version history retained", ].map((note, i) => ( -
+
- {note} + + {note} +
))}
@@ -1354,7 +1448,9 @@ const ViewDocument = (): ReactElement => {

- Executing Action: {selectedWorkflowAction.action||selectedWorkflowAction.name} + Executing Action:{" "} + {selectedWorkflowAction.action || + selectedWorkflowAction.name}