34
apps/console/eslint.config.js
Normal file
34
apps/console/eslint.config.js
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
import globals from "globals";
|
||||||
|
import pluginJs from "@eslint/js";
|
||||||
|
import tseslint from "typescript-eslint";
|
||||||
|
import pluginReact from "eslint-plugin-react";
|
||||||
|
|
||||||
|
|
||||||
|
/** @type {import('eslint').Linter.Config[]} */
|
||||||
|
export default [
|
||||||
|
{files: ["**/*.{js,mjs,cjs,ts,jsx,tsx}"]},
|
||||||
|
{languageOptions: {
|
||||||
|
globals: globals.browser,
|
||||||
|
parserOptions: {
|
||||||
|
ecmaFeatures: {
|
||||||
|
jsx: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}},
|
||||||
|
pluginJs.configs.recommended,
|
||||||
|
...tseslint.configs.recommended,
|
||||||
|
pluginReact.configs.flat.recommended,
|
||||||
|
{
|
||||||
|
settings: {
|
||||||
|
react: {
|
||||||
|
version: "detect"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
"react/react-in-jsx-scope": "off",
|
||||||
|
"react/prop-types": "off",
|
||||||
|
"@typescript-eslint/no-explicit-any": "warn",
|
||||||
|
"react/no-unescaped-entities": "off"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
];
|
||||||
@@ -43,6 +43,7 @@
|
|||||||
"@babel/preset-env": "^7.26.7",
|
"@babel/preset-env": "^7.26.7",
|
||||||
"@babel/preset-typescript": "^7.26.0",
|
"@babel/preset-typescript": "^7.26.0",
|
||||||
"@deanc/esbuild-plugin-postcss": "^1.0.2",
|
"@deanc/esbuild-plugin-postcss": "^1.0.2",
|
||||||
|
"@eslint/js": "^9.21.0",
|
||||||
"@probo/tsconfig": "^0.0.1",
|
"@probo/tsconfig": "^0.0.1",
|
||||||
"@types/babel-core": "^6.25.10",
|
"@types/babel-core": "^6.25.10",
|
||||||
"@types/node": "^22.13.0",
|
"@types/node": "^22.13.0",
|
||||||
@@ -54,9 +55,12 @@
|
|||||||
"babel-plugin-relay": "^18.2.0",
|
"babel-plugin-relay": "^18.2.0",
|
||||||
"esbuild": "^0.25.0",
|
"esbuild": "^0.25.0",
|
||||||
"eslint": "^9.21.0",
|
"eslint": "^9.21.0",
|
||||||
|
"eslint-plugin-react": "^7.37.4",
|
||||||
|
"globals": "^16.0.0",
|
||||||
"relay-compiler": "^18.2.0",
|
"relay-compiler": "^18.2.0",
|
||||||
"tailwindcss": "^3.4.17",
|
"tailwindcss": "^3.4.17",
|
||||||
"typescript": "^5.7.3"
|
"typescript": "^5.7.3",
|
||||||
|
"typescript-eslint": "^8.25.0"
|
||||||
},
|
},
|
||||||
"relay": {
|
"relay": {
|
||||||
"src": "src",
|
"src": "src",
|
||||||
|
|||||||
@@ -4,13 +4,7 @@ import { lazy, StrictMode, Suspense } from "react";
|
|||||||
import { createRoot } from "react-dom/client";
|
import { createRoot } from "react-dom/client";
|
||||||
import { HelmetProvider } from "react-helmet-async";
|
import { HelmetProvider } from "react-helmet-async";
|
||||||
import { RelayEnvironmentProvider } from "react-relay";
|
import { RelayEnvironmentProvider } from "react-relay";
|
||||||
import {
|
import { BrowserRouter, Route, Routes, useLocation } from "react-router";
|
||||||
BrowserRouter,
|
|
||||||
Route,
|
|
||||||
Routes,
|
|
||||||
useLocation,
|
|
||||||
Navigate,
|
|
||||||
} from "react-router";
|
|
||||||
import "App.css";
|
import "App.css";
|
||||||
import ErrorBoundary from "./components/ErrorBoundary";
|
import ErrorBoundary from "./components/ErrorBoundary";
|
||||||
import ConsoleLayout from "./layouts/ConsoleLayout";
|
import ConsoleLayout from "./layouts/ConsoleLayout";
|
||||||
@@ -122,11 +116,22 @@ function App() {
|
|||||||
element={
|
element={
|
||||||
<Suspense>
|
<Suspense>
|
||||||
<ErrorBoundaryWithLocation>
|
<ErrorBoundaryWithLocation>
|
||||||
<CreateOrganizationPage />
|
<ConsoleLayout />
|
||||||
</ErrorBoundaryWithLocation>
|
</ErrorBoundaryWithLocation>
|
||||||
</Suspense>
|
</Suspense>
|
||||||
}
|
}
|
||||||
/>
|
>
|
||||||
|
<Route
|
||||||
|
index
|
||||||
|
element={
|
||||||
|
<Suspense>
|
||||||
|
<ErrorBoundaryWithLocation>
|
||||||
|
<CreateOrganizationPage />
|
||||||
|
</ErrorBoundaryWithLocation>
|
||||||
|
</Suspense>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Route>
|
||||||
|
|
||||||
{/* Organization-specific Routes */}
|
{/* Organization-specific Routes */}
|
||||||
<Route
|
<Route
|
||||||
@@ -147,6 +152,16 @@ function App() {
|
|||||||
</Suspense>
|
</Suspense>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
<Route
|
||||||
|
path="create"
|
||||||
|
element={
|
||||||
|
<Suspense>
|
||||||
|
<ErrorBoundaryWithLocation>
|
||||||
|
<CreateOrganizationPage />
|
||||||
|
</ErrorBoundaryWithLocation>
|
||||||
|
</Suspense>
|
||||||
|
}
|
||||||
|
/>
|
||||||
<Route
|
<Route
|
||||||
path="peoples"
|
path="peoples"
|
||||||
element={
|
element={
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import {
|
|||||||
BookOpen,
|
BookOpen,
|
||||||
Users,
|
Users,
|
||||||
ToyBrick,
|
ToyBrick,
|
||||||
Command,
|
|
||||||
LifeBuoy,
|
LifeBuoy,
|
||||||
Send,
|
Send,
|
||||||
Settings,
|
Settings,
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import {
|
|||||||
import { graphql, useFragment } from "react-relay";
|
import { graphql, useFragment } from "react-relay";
|
||||||
import { useNavigate } from "react-router";
|
import { useNavigate } from "react-router";
|
||||||
|
|
||||||
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
|
import { Avatar, AvatarFallback } from "@/components/ui/avatar";
|
||||||
import {
|
import {
|
||||||
DropdownMenu,
|
DropdownMenu,
|
||||||
DropdownMenuContent,
|
DropdownMenuContent,
|
||||||
@@ -28,10 +28,7 @@ import {
|
|||||||
useSidebar,
|
useSidebar,
|
||||||
} from "@/components/ui/sidebar";
|
} from "@/components/ui/sidebar";
|
||||||
import { useAuth } from "@/contexts/AuthContext";
|
import { useAuth } from "@/contexts/AuthContext";
|
||||||
import {
|
import { NavUser_viewer$key } from "./__generated__/NavUser_viewer.graphql";
|
||||||
NavUser_viewer$key,
|
|
||||||
NavUser_viewer$data,
|
|
||||||
} from "./__generated__/NavUser_viewer.graphql";
|
|
||||||
|
|
||||||
export const navUserFragment = graphql`
|
export const navUserFragment = graphql`
|
||||||
fragment NavUser_viewer on User {
|
fragment NavUser_viewer on User {
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
|
|||||||
response.ok && !response.headers.get("WWW-Authenticate");
|
response.ok && !response.headers.get("WWW-Authenticate");
|
||||||
setIsAuthenticated(authenticated);
|
setIsAuthenticated(authenticated);
|
||||||
return authenticated;
|
return authenticated;
|
||||||
} catch (error) {
|
} catch {
|
||||||
setIsAuthenticated(false);
|
setIsAuthenticated(false);
|
||||||
return false;
|
return false;
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
@@ -1,11 +1,4 @@
|
|||||||
import {
|
import { Link, Outlet, Route, Routes, useParams } from "react-router";
|
||||||
Link,
|
|
||||||
Outlet,
|
|
||||||
Route,
|
|
||||||
Routes,
|
|
||||||
useLocation,
|
|
||||||
useParams,
|
|
||||||
} from "react-router";
|
|
||||||
import { AppSidebar } from "@/components/AppSidebar";
|
import { AppSidebar } from "@/components/AppSidebar";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import {
|
import {
|
||||||
@@ -33,6 +26,26 @@ import { ConsoleLayoutOrganizationQuery } from "./__generated__/ConsoleLayoutOrg
|
|||||||
|
|
||||||
function BreadcrumbHome({ children }: { children: React.ReactNode }) {
|
function BreadcrumbHome({ children }: { children: React.ReactNode }) {
|
||||||
const { organizationId } = useParams();
|
const { organizationId } = useParams();
|
||||||
|
|
||||||
|
// If there's no organizationId, we're on the create organization page
|
||||||
|
if (!organizationId) {
|
||||||
|
return (
|
||||||
|
<Breadcrumb>
|
||||||
|
<BreadcrumbList>
|
||||||
|
<BreadcrumbItem>
|
||||||
|
<BreadcrumbLink asChild>
|
||||||
|
<Link to="/">Home</Link>
|
||||||
|
</BreadcrumbLink>
|
||||||
|
</BreadcrumbItem>
|
||||||
|
<BreadcrumbSeparator />
|
||||||
|
<BreadcrumbItem>
|
||||||
|
<BreadcrumbPage>Create Organization</BreadcrumbPage>
|
||||||
|
</BreadcrumbItem>
|
||||||
|
</BreadcrumbList>
|
||||||
|
</Breadcrumb>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const data = useLazyLoadQuery<ConsoleLayoutOrganizationQuery>(
|
const data = useLazyLoadQuery<ConsoleLayoutOrganizationQuery>(
|
||||||
graphql`
|
graphql`
|
||||||
query ConsoleLayoutOrganizationQuery($organizationId: ID!) {
|
query ConsoleLayoutOrganizationQuery($organizationId: ID!) {
|
||||||
@@ -223,17 +236,6 @@ function BreadcrumbPeopleOverview() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function BreadcrumbCreateOrganization() {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<BreadcrumbSeparator />
|
|
||||||
<BreadcrumbItem>
|
|
||||||
<BreadcrumbPage>Create Organization</BreadcrumbPage>
|
|
||||||
</BreadcrumbItem>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function BreadcrumbControlOverview() {
|
function BreadcrumbControlOverview() {
|
||||||
const { organizationId, frameworkId, controlId } = useParams();
|
const { organizationId, frameworkId, controlId } = useParams();
|
||||||
const data = useLazyLoadQuery<ConsoleLayoutBreadcrumbControlOverviewQuery>(
|
const data = useLazyLoadQuery<ConsoleLayoutBreadcrumbControlOverviewQuery>(
|
||||||
@@ -289,6 +291,7 @@ function BreadcrumbControlOverview() {
|
|||||||
|
|
||||||
export default function ConsoleLayout() {
|
export default function ConsoleLayout() {
|
||||||
const { organizationId } = useParams();
|
const { organizationId } = useParams();
|
||||||
|
const showBreadcrumb = !!organizationId;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SidebarProvider>
|
<SidebarProvider>
|
||||||
@@ -299,31 +302,36 @@ export default function ConsoleLayout() {
|
|||||||
<SidebarTrigger className="-ml-1" />
|
<SidebarTrigger className="-ml-1" />
|
||||||
<Separator orientation="vertical" className="mr-2 h-4" />
|
<Separator orientation="vertical" className="mr-2 h-4" />
|
||||||
<BreadcrumbHome>
|
<BreadcrumbHome>
|
||||||
<Routes>
|
{showBreadcrumb && (
|
||||||
<Route path="frameworks" element={<BreadcrumbFrameworkList />}>
|
<Routes>
|
||||||
<Route
|
<Route
|
||||||
path=":frameworkId"
|
path="frameworks"
|
||||||
element={<BreadcrumbFrameworkOverview />}
|
element={<BreadcrumbFrameworkList />}
|
||||||
/>
|
>
|
||||||
<Route
|
<Route
|
||||||
path=":frameworkId/controls/:controlId"
|
path=":frameworkId"
|
||||||
element={<BreadcrumbControlOverview />}
|
element={<BreadcrumbFrameworkOverview />}
|
||||||
/>
|
/>
|
||||||
</Route>
|
<Route
|
||||||
<Route path="peoples" element={<BreadcrumbPeopleList />}>
|
path=":frameworkId/controls/:controlId"
|
||||||
<Route
|
element={<BreadcrumbControlOverview />}
|
||||||
path=":peopleId"
|
/>
|
||||||
element={<BreadcrumbPeopleOverview />}
|
</Route>
|
||||||
/>
|
<Route path="peoples" element={<BreadcrumbPeopleList />}>
|
||||||
<Route path="create" element={<BreadcrumbCreatePeople />} />
|
<Route
|
||||||
</Route>
|
path=":peopleId"
|
||||||
<Route path="vendors" element={<BreadcrumbVendorList />}>
|
element={<BreadcrumbPeopleOverview />}
|
||||||
<Route
|
/>
|
||||||
path=":vendorId"
|
<Route path="create" element={<BreadcrumbCreatePeople />} />
|
||||||
element={<BreadcrumbVendorOverview />}
|
</Route>
|
||||||
/>
|
<Route path="vendors" element={<BreadcrumbVendorList />}>
|
||||||
</Route>
|
<Route
|
||||||
</Routes>
|
path=":vendorId"
|
||||||
|
element={<BreadcrumbVendorOverview />}
|
||||||
|
/>
|
||||||
|
</Route>
|
||||||
|
</Routes>
|
||||||
|
)}
|
||||||
</BreadcrumbHome>
|
</BreadcrumbHome>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|||||||
@@ -143,12 +143,12 @@ export default function CreateOrganizationPage() {
|
|||||||
<title>Create Organization - Probo</title>
|
<title>Create Organization - Probo</title>
|
||||||
</Helmet>
|
</Helmet>
|
||||||
|
|
||||||
<div className="container mx-auto p-6 space-y-6">
|
<div className="space-y-6">
|
||||||
<div>
|
<div>
|
||||||
<h1 className="text-4xl font-medium tracking-tight">
|
<h1 className="text-2xl font-semibold tracking-tight">
|
||||||
Create Organization
|
Create Organization
|
||||||
</h1>
|
</h1>
|
||||||
<p className="text-lg text-muted-foreground">
|
<p className="text-muted-foreground">
|
||||||
Create a new organization to manage your compliance and security
|
Create a new organization to manage your compliance and security
|
||||||
needs.
|
needs.
|
||||||
</p>
|
</p>
|
||||||
|
|||||||
@@ -1,14 +1,6 @@
|
|||||||
import { Suspense, useEffect, useState } from "react";
|
import { Suspense, useState } from "react";
|
||||||
import { useNavigate, useParams } from "react-router";
|
import { useNavigate, useParams } from "react-router";
|
||||||
import {
|
import { graphql, useMutation, ConnectionHandler } from "react-relay";
|
||||||
graphql,
|
|
||||||
PreloadedQuery,
|
|
||||||
usePreloadedQuery,
|
|
||||||
useQueryLoader,
|
|
||||||
useMutation,
|
|
||||||
useRelayEnvironment,
|
|
||||||
ConnectionHandler,
|
|
||||||
} from "react-relay";
|
|
||||||
import { Helmet } from "react-helmet-async";
|
import { Helmet } from "react-helmet-async";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Card } from "@/components/ui/card";
|
import { Card } from "@/components/ui/card";
|
||||||
@@ -86,7 +78,7 @@ function CreatePeoplePageContent() {
|
|||||||
kind: "EMPLOYEE" as "EMPLOYEE" | "CONTRACTOR",
|
kind: "EMPLOYEE" as "EMPLOYEE" | "CONTRACTOR",
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleFieldChange = (field: keyof typeof formData, value: any) => {
|
const handleFieldChange = (field: keyof typeof formData, value: unknown) => {
|
||||||
setFormData((prev) => ({
|
setFormData((prev) => ({
|
||||||
...prev,
|
...prev,
|
||||||
[field]: value,
|
[field]: value,
|
||||||
|
|||||||
@@ -6,10 +6,9 @@ import {
|
|||||||
usePreloadedQuery,
|
usePreloadedQuery,
|
||||||
useQueryLoader,
|
useQueryLoader,
|
||||||
} from "react-relay";
|
} from "react-relay";
|
||||||
import { Shield, FileText, Clock, MoveUpRight } from "lucide-react";
|
import { Shield, MoveUpRight, Clock } from "lucide-react";
|
||||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
import { Card, CardContent } from "@/components/ui/card";
|
||||||
import { Progress } from "@/components/ui/progress";
|
import { Avatar, AvatarFallback } from "@/components/ui/avatar";
|
||||||
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
|
|
||||||
import type { FrameworkOverviewPageQuery as FrameworkOverviewPageQueryType } from "./__generated__/FrameworkOverviewPageQuery.graphql";
|
import type { FrameworkOverviewPageQuery as FrameworkOverviewPageQueryType } from "./__generated__/FrameworkOverviewPageQuery.graphql";
|
||||||
import { Helmet } from "react-helmet-async";
|
import { Helmet } from "react-helmet-async";
|
||||||
import { createPortal } from "react-dom";
|
import { createPortal } from "react-dom";
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { Link, useLocation, useNavigate } from "react-router";
|
import { Link, useNavigate } from "react-router";
|
||||||
import { Helmet } from "react-helmet-async";
|
|
||||||
import { Button } from "@/components/ui/button";
|
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";
|
||||||
@@ -14,12 +13,8 @@ export default function LoginPage() {
|
|||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
const { toast } = useToast();
|
const { toast } = useToast();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const location = useLocation();
|
|
||||||
const { checkAuth } = useAuth();
|
const { checkAuth } = useAuth();
|
||||||
|
|
||||||
// Get the redirect path from location state or default to home
|
|
||||||
const from = location.state?.from?.pathname || "/";
|
|
||||||
|
|
||||||
const handleSubmit = async (e: React.FormEvent) => {
|
const handleSubmit = async (e: React.FormEvent) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
@@ -50,10 +45,10 @@ export default function LoginPage() {
|
|||||||
} else {
|
} else {
|
||||||
throw new Error("Authentication failed");
|
throw new Error("Authentication failed");
|
||||||
}
|
}
|
||||||
} catch (error: any) {
|
} catch (error: unknown) {
|
||||||
toast({
|
toast({
|
||||||
title: "Error",
|
title: "Error",
|
||||||
description: error.message || "Failed to login",
|
description: error instanceof Error ? error.message : "Failed to login",
|
||||||
variant: "destructive",
|
variant: "destructive",
|
||||||
});
|
});
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { useEffect, useState } from "react";
|
import { useEffect } from "react";
|
||||||
import { useNavigate } from "react-router";
|
import { useNavigate } from "react-router";
|
||||||
import { graphql, useLazyLoadQuery } from "react-relay";
|
import { graphql, useLazyLoadQuery } from "react-relay";
|
||||||
import { Helmet } from "react-helmet-async";
|
import { Helmet } from "react-helmet-async";
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import {
|
|||||||
usePaginationFragment,
|
usePaginationFragment,
|
||||||
} from "react-relay";
|
} from "react-relay";
|
||||||
import { useSearchParams, useParams } from "react-router";
|
import { useSearchParams, useParams } from "react-router";
|
||||||
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
|
import { Avatar, AvatarFallback } from "@/components/ui/avatar";
|
||||||
import { Badge } from "@/components/ui/badge";
|
import { Badge } from "@/components/ui/badge";
|
||||||
import { UserPlus, Trash2, ChevronRight } from "lucide-react";
|
import { UserPlus, Trash2, ChevronRight } from "lucide-react";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
@@ -136,8 +136,8 @@ function PeopleListContent({
|
|||||||
peopleListPageQuery,
|
peopleListPageQuery,
|
||||||
queryRef
|
queryRef
|
||||||
);
|
);
|
||||||
const [searchParams, setSearchParams] = useSearchParams();
|
const [, setSearchParams] = useSearchParams();
|
||||||
const [isPending, startTransition] = useTransition();
|
const [, startTransition] = useTransition();
|
||||||
const [deletePeople] =
|
const [deletePeople] =
|
||||||
useMutation<PeopleListPageDeletePeopleMutation>(deletePeopleMutation);
|
useMutation<PeopleListPageDeletePeopleMutation>(deletePeopleMutation);
|
||||||
const { organizationId } = useParams();
|
const { organizationId } = useParams();
|
||||||
|
|||||||
@@ -95,8 +95,8 @@ function PeopleOverviewPageContent({
|
|||||||
kind: data.node.kind,
|
kind: data.node.kind,
|
||||||
});
|
});
|
||||||
const [commit] = useMutation(updatePeopleMutation);
|
const [commit] = useMutation(updatePeopleMutation);
|
||||||
const [_, loadQuery] = useQueryLoader<PeopleOverviewPageQueryType>(
|
const [, loadQuery] = useQueryLoader<PeopleOverviewPageQueryType>(
|
||||||
peopleOverviewPageQuery,
|
peopleOverviewPageQuery
|
||||||
);
|
);
|
||||||
const { toast } = useToast();
|
const { toast } = useToast();
|
||||||
|
|
||||||
@@ -139,7 +139,7 @@ function PeopleOverviewPageContent({
|
|||||||
});
|
});
|
||||||
}, [commit, data.node.id, data.node.version, formData, loadQuery, toast]);
|
}, [commit, data.node.id, data.node.version, formData, loadQuery, toast]);
|
||||||
|
|
||||||
const handleFieldChange = (field: keyof typeof formData, value: any) => {
|
const handleFieldChange = (field: keyof typeof formData, value: unknown) => {
|
||||||
setFormData((prev) => ({
|
setFormData((prev) => ({
|
||||||
...prev,
|
...prev,
|
||||||
[field]: value,
|
[field]: value,
|
||||||
@@ -198,7 +198,7 @@ function PeopleOverviewPageContent({
|
|||||||
onClick={() => {
|
onClick={() => {
|
||||||
const newEmails =
|
const newEmails =
|
||||||
formData.additionalEmailAddresses.filter(
|
formData.additionalEmailAddresses.filter(
|
||||||
(_, i) => i !== index,
|
(_, i) => i !== index
|
||||||
);
|
);
|
||||||
handleFieldChange("additionalEmailAddresses", newEmails);
|
handleFieldChange("additionalEmailAddresses", newEmails);
|
||||||
}}
|
}}
|
||||||
@@ -243,7 +243,7 @@ function PeopleOverviewPageContent({
|
|||||||
"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 === "EMPLOYEE"
|
||||||
? "bg-blue-100 text-blue-900 ring-2 ring-blue-600 ring-offset-2"
|
? "bg-blue-100 text-blue-900 ring-2 ring-blue-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
|
Employee
|
||||||
@@ -254,7 +254,7 @@ function PeopleOverviewPageContent({
|
|||||||
"rounded-full px-4 py-1 text-sm transition-colors",
|
"rounded-full px-4 py-1 text-sm transition-colors",
|
||||||
formData.kind === "CONTRACTOR"
|
formData.kind === "CONTRACTOR"
|
||||||
? "bg-purple-100 text-purple-900 ring-2 ring-purple-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"
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
Contractor
|
Contractor
|
||||||
@@ -303,7 +303,7 @@ function PeopleOverviewPageFallback() {
|
|||||||
export default function PeopleOverviewPage() {
|
export default function PeopleOverviewPage() {
|
||||||
const { peopleId } = useParams();
|
const { peopleId } = useParams();
|
||||||
const [queryRef, loadQuery] = useQueryLoader<PeopleOverviewPageQueryType>(
|
const [queryRef, loadQuery] = useQueryLoader<PeopleOverviewPageQueryType>(
|
||||||
peopleOverviewPageQuery,
|
peopleOverviewPageQuery
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import {
|
|||||||
CardTitle,
|
CardTitle,
|
||||||
CardDescription,
|
CardDescription,
|
||||||
} from "@/components/ui/card";
|
} from "@/components/ui/card";
|
||||||
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
|
import { Avatar, AvatarFallback } from "@/components/ui/avatar";
|
||||||
import {
|
import {
|
||||||
DropdownMenu,
|
DropdownMenu,
|
||||||
DropdownMenuContent,
|
DropdownMenuContent,
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import {
|
|||||||
usePaginationFragment,
|
usePaginationFragment,
|
||||||
} from "react-relay";
|
} from "react-relay";
|
||||||
import { useSearchParams, useParams } from "react-router";
|
import { useSearchParams, useParams } from "react-router";
|
||||||
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
|
import { Avatar, AvatarFallback } from "@/components/ui/avatar";
|
||||||
import { Badge } from "@/components/ui/badge";
|
import { Badge } from "@/components/ui/badge";
|
||||||
import { Store, ChevronRight, Trash2 } from "lucide-react";
|
import { Store, ChevronRight, Trash2 } from "lucide-react";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
@@ -190,8 +190,8 @@ function VendorListContent({
|
|||||||
vendorListPageQuery,
|
vendorListPageQuery,
|
||||||
queryRef
|
queryRef
|
||||||
);
|
);
|
||||||
const [searchParams, setSearchParams] = useSearchParams();
|
const [, setSearchParams] = useSearchParams();
|
||||||
const [isPending, startTransition] = useTransition();
|
const [, startTransition] = useTransition();
|
||||||
const [searchTerm, setSearchTerm] = useState("");
|
const [searchTerm, setSearchTerm] = useState("");
|
||||||
const [filteredVendors, setFilteredVendors] = useState<VendorItem[]>([]);
|
const [filteredVendors, setFilteredVendors] = useState<VendorItem[]>([]);
|
||||||
const [createVendor] =
|
const [createVendor] =
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { Label } from "@/components/ui/label";
|
|||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { useToast } from "@/hooks/use-toast";
|
import { useToast } from "@/hooks/use-toast";
|
||||||
import { HelpCircle, ArrowUpRight, Pencil, Check, X } from "lucide-react";
|
import { HelpCircle } from "lucide-react";
|
||||||
import {
|
import {
|
||||||
graphql,
|
graphql,
|
||||||
PreloadedQuery,
|
PreloadedQuery,
|
||||||
@@ -13,7 +13,7 @@ import {
|
|||||||
useQueryLoader,
|
useQueryLoader,
|
||||||
useMutation,
|
useMutation,
|
||||||
} from "react-relay";
|
} from "react-relay";
|
||||||
import { Suspense, useEffect, useState, useCallback, useMemo } from "react";
|
import { Suspense, useEffect, useState, useCallback } from "react";
|
||||||
import type { VendorOverviewPageQuery as VendorOverviewPageQueryType } from "./__generated__/VendorOverviewPageQuery.graphql";
|
import type { VendorOverviewPageQuery as VendorOverviewPageQueryType } from "./__generated__/VendorOverviewPageQuery.graphql";
|
||||||
import { useParams } from "react-router";
|
import { useParams } from "react-router";
|
||||||
import { Helmet } from "react-helmet-async";
|
import { Helmet } from "react-helmet-async";
|
||||||
@@ -124,8 +124,8 @@ function VendorOverviewPageContent({
|
|||||||
privacyPolicyUrl: data.node.privacyPolicyUrl || "",
|
privacyPolicyUrl: data.node.privacyPolicyUrl || "",
|
||||||
});
|
});
|
||||||
const [commit] = useMutation(updateVendorMutation);
|
const [commit] = useMutation(updateVendorMutation);
|
||||||
const [_, loadQuery] = useQueryLoader<VendorOverviewPageQueryType>(
|
const [, loadQuery] = useQueryLoader<VendorOverviewPageQueryType>(
|
||||||
vendorOverviewPageQuery,
|
vendorOverviewPageQuery
|
||||||
);
|
);
|
||||||
const { toast } = useToast();
|
const { toast } = useToast();
|
||||||
|
|
||||||
@@ -174,13 +174,10 @@ function VendorOverviewPageContent({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
updater: (store) => {
|
|
||||||
// Clear any error states if needed
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
}, [commit, data.node.id, data.node.version, formData, loadQuery, toast]);
|
}, [commit, data.node.id, data.node.version, formData, loadQuery, toast]);
|
||||||
|
|
||||||
const handleFieldChange = (field: keyof typeof formData, value: any) => {
|
const handleFieldChange = (field: keyof typeof formData, value: unknown) => {
|
||||||
setFormData((prev) => ({
|
setFormData((prev) => ({
|
||||||
...prev,
|
...prev,
|
||||||
[field]: value,
|
[field]: value,
|
||||||
@@ -262,7 +259,7 @@ function VendorOverviewPageContent({
|
|||||||
"rounded-full px-4 py-1 text-sm transition-colors",
|
"rounded-full px-4 py-1 text-sm transition-colors",
|
||||||
formData.serviceCriticality === "LOW"
|
formData.serviceCriticality === "LOW"
|
||||||
? "bg-green-100 text-green-900 ring-2 ring-green-600 ring-offset-2"
|
? "bg-green-100 text-green-900 ring-2 ring-green-600 ring-offset-2"
|
||||||
: "bg-gray-100 text-gray-900 hover:bg-gray-200",
|
: "bg-gray-100 text-gray-900 hover:bg-gray-200"
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
Low
|
Low
|
||||||
@@ -275,7 +272,7 @@ function VendorOverviewPageContent({
|
|||||||
"rounded-full px-4 py-1 text-sm transition-colors",
|
"rounded-full px-4 py-1 text-sm transition-colors",
|
||||||
formData.serviceCriticality === "MEDIUM"
|
formData.serviceCriticality === "MEDIUM"
|
||||||
? "bg-yellow-100 text-yellow-900 ring-2 ring-yellow-600 ring-offset-2"
|
? "bg-yellow-100 text-yellow-900 ring-2 ring-yellow-600 ring-offset-2"
|
||||||
: "bg-gray-100 text-gray-900 hover:bg-gray-200",
|
: "bg-gray-100 text-gray-900 hover:bg-gray-200"
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
Medium
|
Medium
|
||||||
@@ -288,7 +285,7 @@ function VendorOverviewPageContent({
|
|||||||
"rounded-full px-4 py-1 text-sm transition-colors",
|
"rounded-full px-4 py-1 text-sm transition-colors",
|
||||||
formData.serviceCriticality === "HIGH"
|
formData.serviceCriticality === "HIGH"
|
||||||
? "bg-red-100 text-red-900 ring-2 ring-red-600 ring-offset-2"
|
? "bg-red-100 text-red-900 ring-2 ring-red-600 ring-offset-2"
|
||||||
: "bg-gray-100 text-gray-900 hover:bg-gray-200",
|
: "bg-gray-100 text-gray-900 hover:bg-gray-200"
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
High
|
High
|
||||||
@@ -316,7 +313,7 @@ function VendorOverviewPageContent({
|
|||||||
"rounded-full px-4 py-1 text-sm transition-colors",
|
"rounded-full px-4 py-1 text-sm transition-colors",
|
||||||
formData.riskTier === "CRITICAL"
|
formData.riskTier === "CRITICAL"
|
||||||
? "bg-red-100 text-red-900 ring-2 ring-red-600 ring-offset-2"
|
? "bg-red-100 text-red-900 ring-2 ring-red-600 ring-offset-2"
|
||||||
: "bg-gray-100 text-gray-900 hover:bg-gray-200",
|
: "bg-gray-100 text-gray-900 hover:bg-gray-200"
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
Critical
|
Critical
|
||||||
@@ -329,7 +326,7 @@ function VendorOverviewPageContent({
|
|||||||
"rounded-full px-4 py-1 text-sm transition-colors",
|
"rounded-full px-4 py-1 text-sm transition-colors",
|
||||||
formData.riskTier === "SIGNIFICANT"
|
formData.riskTier === "SIGNIFICANT"
|
||||||
? "bg-yellow-100 text-yellow-900 ring-2 ring-yellow-600 ring-offset-2"
|
? "bg-yellow-100 text-yellow-900 ring-2 ring-yellow-600 ring-offset-2"
|
||||||
: "bg-gray-100 text-gray-900 hover:bg-gray-200",
|
: "bg-gray-100 text-gray-900 hover:bg-gray-200"
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
Significant
|
Significant
|
||||||
@@ -340,7 +337,7 @@ function VendorOverviewPageContent({
|
|||||||
"rounded-full px-4 py-1 text-sm transition-colors",
|
"rounded-full px-4 py-1 text-sm transition-colors",
|
||||||
formData.riskTier === "GENERAL"
|
formData.riskTier === "GENERAL"
|
||||||
? "bg-green-100 text-green-900 ring-2 ring-green-600 ring-offset-2"
|
? "bg-green-100 text-green-900 ring-2 ring-green-600 ring-offset-2"
|
||||||
: "bg-gray-100 text-gray-900 hover:bg-gray-200",
|
: "bg-gray-100 text-gray-900 hover:bg-gray-200"
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
General
|
General
|
||||||
@@ -421,7 +418,7 @@ function VendorOverviewPageFallback() {
|
|||||||
export default function VendorOverviewPage() {
|
export default function VendorOverviewPage() {
|
||||||
const { vendorId } = useParams();
|
const { vendorId } = useParams();
|
||||||
const [queryRef, loadQuery] = useQueryLoader<VendorOverviewPageQueryType>(
|
const [queryRef, loadQuery] = useQueryLoader<VendorOverviewPageQueryType>(
|
||||||
vendorOverviewPageQuery,
|
vendorOverviewPageQuery
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
Reference in New Issue
Block a user