Add UI input & textarea

Signed-off-by: Bryan Frimin <bryan@getprobo.com>
Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
This commit is contained in:
Jonathan
2025-05-21 11:02:49 +02:00
committed by Sacha Al Himdani
parent a86181c83d
commit 15cf471454
35 changed files with 3094 additions and 14933 deletions

View File

@@ -0,0 +1,15 @@
import { useQuery, type UseQueryOptions } from "@tanstack/react-query";
/**
* Wrapper around useQuery to fetch data from an API endpoint
*/
export function useFetchQuery<T>(
url: string,
options?: Omit<UseQueryOptions<T>, "queryKey" | "queryFn">
) {
return useQuery<T>({
...options,
queryKey: [url],
queryFn: () => fetch(url).then((res) => res.json()),
});
}

View File

@@ -1,16 +1,27 @@
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import "./index.css";
import { App } from "./App.tsx";
import { RelayProvider } from "./providers/RelayProviders.tsx";
import { TranslatorProvider } from "./providers/TranslatorProvider.tsx";
import { App } from "./App";
import { RelayProvider } from "./providers/RelayProviders";
import { TranslatorProvider } from "./providers/TranslatorProvider";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
const queryClient = new QueryClient({
defaultOptions: {
queries: {
refetchOnWindowFocus: false,
},
},
});
createRoot(document.getElementById("root")!).render(
<StrictMode>
<RelayProvider>
<TranslatorProvider>
<App />
</TranslatorProvider>
</RelayProvider>
<QueryClientProvider client={queryClient}>
<RelayProvider>
<TranslatorProvider>
<App />
</TranslatorProvider>
</RelayProvider>
</QueryClientProvider>
</StrictMode>
);

View File

@@ -5,28 +5,58 @@ import {
Dialog,
DialogContent,
DialogFooter,
Label,
Select,
Option,
Field,
} from "@probo/ui";
import type { ReactNode } from "react";
import { useMemo, useState, type ReactNode } from "react";
import { useFetchQuery } from "../../../hooks/useFetchQuery";
type Props = {
trigger: ReactNode;
};
type Risk = {
category: string;
name: string;
description: string;
};
export default function NewRiskDialog({ trigger }: Props) {
const { __ } = useTranslate();
const onTemplateChange = (risk: Risk) => {
console.log(risk);
};
return (
<Dialog
open
onClose={() => {}}
trigger={trigger}
title={<Breadcrumb items={[__("Risks"), __("New Risk")]}></Breadcrumb>}
>
<form>
<DialogContent>
Lorem ipsum dolor, sit amet consectetur adipisicing elit. Velit harum
maiores dolor hic nisi repudiandae autem quod rem eveniet iure
quibusdam sint iste numquam, suscipit ad delectus aspernatur. Quo,
amet.
<DialogContent className="grid grid-cols-[1fr_420px]">
{/* Main form */}
<div className="py-8 px-12 space-y-6">
<TemplateSelector onChange={onTemplateChange} />
<Field
label={__("Risk name")}
name="name"
placeholder={__("Service Outage")}
/>
<Field
label={__("Description")}
name="description"
placeholder={__("Type your description here")}
type="textarea"
/>
</div>
{/* Properties form */}
<div className="py-5 px-6 bg-subtle"></div>
</DialogContent>
<DialogFooter>
<Button type="submit">{__("Create risk")}</Button>
@@ -35,3 +65,58 @@ export default function NewRiskDialog({ trigger }: Props) {
</Dialog>
);
}
function TemplateSelector({ onChange }: { onChange: (risk: Risk) => void }) {
const { __ } = useTranslate();
const { data: risks } = useFetchQuery<Risk[]>("/data/risks/risks.json", {
staleTime: 100_000,
});
const categories = useMemo(
() => Array.from(new Set(risks?.map((t) => t.category))),
[risks]
);
const [selectedCategory, setSelectedCategory] = useState<string | null>(null);
const templates = useMemo(
() => risks?.filter((r) => r.category === selectedCategory) ?? [],
[risks, selectedCategory]
);
const onTemplateChange = (template: string) => {
const risk = risks?.find((r) => r.name === template);
if (!risk) {
throw new Error("Risk not found");
}
onChange(risk);
};
return (
<div>
<Label>{__("Risk category")}</Label>
<div className="grid grid-cols-2 gap-2 mt-[6px]">
<Select
placeholder={__("Select a category")}
onChange={setSelectedCategory}
>
{categories.map((category) => (
<Option key={category} value={category}>
{category}
</Option>
))}
</Select>
<Select
empty={templates?.length === 0}
placeholder={__("Select template")}
onChange={onTemplateChange}
>
{templates?.map((template) => (
<Option key={template.name} value={template.name}>
{template.name}
</Option>
))}
</Select>
</div>
</div>
);
}