Spaces:
Sleeping
Sleeping
| import React, { useState, useMemo, useEffect } from 'react'; | |
| import { | |
| Table, | |
| TableBody, | |
| TableCaption, | |
| TableCell, | |
| TableFooter, | |
| TableHead, | |
| TableHeader, | |
| TableRow, | |
| } from "@/components/ui/table" | |
| import { useRecoilValue } from 'recoil'; | |
| import { callFnState } from '@chainlit/react-client'; | |
| import { | |
| Card, | |
| CardAction, | |
| CardContent, | |
| CardDescription, | |
| CardFooter, | |
| CardHeader, | |
| CardTitle, | |
| } from "@/components/ui/card" | |
| import { ClipboardPaste, Plus, Lock, LockOpen } from 'lucide-react' | |
| import { Button } from "@/components/ui/button" | |
| import { Input } from "@/components/ui/input" | |
| // Internal Badge component to avoid import errors | |
| const Badge = ({ children, className }) => ( | |
| <span className={`bg-pii text-blue-800 rounded-full px-2 py-1 mx-1 transition-colors text-main-700 ${className}`}> | |
| {children} | |
| </span> | |
| ); | |
| function PII() { | |
| // const dic = props.dic || {}; | |
| const [dic, setDict] = useState(props.dic || {}); | |
| const [searchTerm, setSearchTerm] = useState(''); | |
| const [dictionary, setDictionary] = useState({}); | |
| const [systemLang, setSystemLang] = useState((typeof navigator !== 'undefined' && (navigator.language || navigator.userLanguage)) ? (navigator.language || navigator.userLanguage).split('-')[0] : 'en'); | |
| const callFn = useRecoilValue(callFnState); | |
| const handleUpdatePii = (key, value) => { | |
| callAction( | |
| { | |
| name: "update_pii", payload: { | |
| key: key, | |
| value: value | |
| } | |
| } | |
| ) | |
| setDict({ ...dic, [key]: { ...dic[key], locked: !dic[key]['locked'] } }); | |
| }; | |
| useEffect(() => { | |
| // Fetch the file from the public path | |
| fetch('/public/dictionnaire.json') | |
| .then(response => response.json()) // Parse the response as JSON | |
| .then(data => setDictionary(data)) // Set the data in state | |
| .catch(error => console.error('Error fetching JSON:', error)); | |
| }, []); | |
| // Determine system language | |
| // const systemLang = (typeof navigator !== 'undefined' && (navigator.language || navigator.userLanguage)) ? (navigator.language || navigator.userLanguage).split('-')[0] : 'en'; | |
| // Enrich dictionary with localized types | |
| function getType(typ) { | |
| if (dictionary[typ]) { | |
| return dictionary[typ][systemLang] || dictionary[typ]['en'] || typ; | |
| } | |
| return typ; | |
| } | |
| const enrichedDic = useMemo(() => { | |
| return Object.entries(dic).reduce((acc, [key, value]) => { | |
| const typeKey = value.type; | |
| const dictEntry = dictionary[typeKey]; | |
| const localizedType = dictEntry | |
| ? (dictEntry[systemLang] || dictEntry['en'] || typeKey) | |
| : typeKey; | |
| acc[key] = { ...value, typ: localizedType }; | |
| return acc; | |
| }, {}); | |
| }, [dic, systemLang]); | |
| const filteredDic = useMemo(() => { | |
| if (searchTerm.trim() === '') { | |
| return enrichedDic; | |
| } | |
| return Object.fromEntries( | |
| Object.entries(enrichedDic).filter(([key, value]) => | |
| String(value.type).toLowerCase().includes(searchTerm.toLowerCase()) || | |
| String(value.value).toLowerCase().includes(searchTerm.toLowerCase()) | |
| ) | |
| ); | |
| }, [searchTerm, enrichedDic]); | |
| const handlInsert = (value) => { | |
| console.log("value", value); | |
| const chatInput = document.getElementById('chat-input'); | |
| if (chatInput && chatInput instanceof HTMLTextAreaElement) { | |
| const start = chatInput.selectionStart; | |
| const end = chatInput.selectionEnd; | |
| const currentValue = chatInput.value; | |
| const spaceToAdd = " "; | |
| const newValue = currentValue.substring(0, start) + spaceToAdd + value + spaceToAdd + currentValue.substring(end); | |
| chatInput.value = newValue; | |
| chatInput.selectionStart = chatInput.selectionEnd = start + spaceToAdd.length + String(value).length + spaceToAdd.length; | |
| chatInput.focus(); | |
| } else { | |
| console.warn('Textarea with id "chat-input" not found.'); | |
| } | |
| } | |
| return ( | |
| <div className='flex flex-col gap-4'> | |
| <div className="relative"> | |
| <svg className="absolute left-2 top-1/2 -translate-y-1/2 h-4 w-4 text-gray-500" fill="none" viewBox="0 0 24 24" stroke="currentColor"> | |
| <path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" /> | |
| </svg> | |
| <Input | |
| type="text" | |
| placeholder="Rechercher" | |
| value={searchTerm} | |
| onChange={(e) => setSearchTerm(e.target.value)} | |
| className="pl-8" | |
| /> | |
| </div> | |
| {Object.entries(filteredDic).map(([key, value]) => ( | |
| <div className='flex flex-col p-4 border-2 rounded-xl gap-2' key={key}> | |
| <span className="text-primary-700">{value.value}</span> | |
| <div className='flex flex-row justify-between'> | |
| <Badge>{getType(value.type)}</Badge> | |
| <div className='flex flex-row gap-2'> | |
| <Button variant="outline" className={value.locked ? "p-2 h-8 rounded-full bg-locked" : "p-2 h-8 rounded-full bg-unlocked"} onClick={() => handleUpdatePii(`${key}`, value.value)}> | |
| {value.locked ? <Lock className="font-bold" /> : <LockOpen className="font-bold" />} | |
| </Button> | |
| <Button variant="outline" className="p-2 h-8 w-8 rounded-full" onClick={() => handlInsert(value.locked ? `[${value.type}_${key}]` : value.value)}> | |
| <Plus className="font-bold" /> | |
| </Button> | |
| </div> | |
| </div> | |
| </div> | |
| ))} | |
| </div> | |
| ) | |
| }; | |
| export default PII; | |