mirror of
https://github.com/cssgunc/compass.git
synced 2025-04-21 18:59:49 -04:00
Compare commits
No commits in common. "dff05af79c002872b9cbc76ed3ef9d2f2438feb0" and "a516c414f64a4c263490dc761a68848fce4c02e2" have entirely different histories.
dff05af79c
...
a516c414f6
|
@ -1,233 +0,0 @@
|
||||||
import { Dispatch, FunctionComponent, ReactNode, SetStateAction } from "react";
|
|
||||||
import React, { useState } from "react";
|
|
||||||
import { ChevronDoubleLeftIcon } from "@heroicons/react/24/solid";
|
|
||||||
import {
|
|
||||||
ArrowsPointingOutIcon,
|
|
||||||
ArrowsPointingInIcon,
|
|
||||||
} from "@heroicons/react/24/outline";
|
|
||||||
import TagsInput from "../TagsInput/Index";
|
|
||||||
import { Details } from "./Drawer";
|
|
||||||
|
|
||||||
type CreateDrawerProps = {
|
|
||||||
details: Details[];
|
|
||||||
onCreate: (newItem: any) => boolean;
|
|
||||||
};
|
|
||||||
|
|
||||||
const CreateDrawer: FunctionComponent<CreateDrawerProps> = ({
|
|
||||||
details,
|
|
||||||
onCreate,
|
|
||||||
}: CreateDrawerProps) => {
|
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
|
||||||
const [isFull, setIsFull] = useState(false);
|
|
||||||
const [newItemContent, setNewItemContent] = useState<any>({});
|
|
||||||
const [renderKey, setRenderKey] = useState(0);
|
|
||||||
|
|
||||||
const handleContentChange = (
|
|
||||||
e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>
|
|
||||||
) => {
|
|
||||||
const { name, value } = e.target;
|
|
||||||
console.log(newItemContent);
|
|
||||||
console.log(Object.keys(newItemContent).length);
|
|
||||||
setNewItemContent((prev: any) => ({
|
|
||||||
...prev,
|
|
||||||
[name]: value,
|
|
||||||
}));
|
|
||||||
};
|
|
||||||
|
|
||||||
const initializeSelectField = (key: string) => {
|
|
||||||
if (!newItemContent[key]) {
|
|
||||||
setNewItemContent((prev: any) => ({
|
|
||||||
...prev,
|
|
||||||
[key]: [],
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleCreate = () => {
|
|
||||||
if (onCreate(newItemContent)) {
|
|
||||||
setNewItemContent({});
|
|
||||||
setIsOpen(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const toggleDrawer = () => {
|
|
||||||
setIsOpen(!isOpen);
|
|
||||||
if (isFull) {
|
|
||||||
setIsFull(!isFull);
|
|
||||||
}
|
|
||||||
if (!isOpen) {
|
|
||||||
setRenderKey((prev) => prev + 1);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const toggleDrawerFullScreen = () => setIsFull(!isFull);
|
|
||||||
|
|
||||||
const drawerClassName = `fixed top-0 right-0 h-full bg-white transform ease-in-out duration-300 z-20 overflow-y-auto ${
|
|
||||||
isOpen ? "translate-x-0 shadow-2xl" : "translate-x-full"
|
|
||||||
} ${isFull ? "w-full" : "w-[600px]"}`;
|
|
||||||
|
|
||||||
const iconComponent = isFull ? (
|
|
||||||
<ArrowsPointingInIcon className="h-5 w-5" />
|
|
||||||
) : (
|
|
||||||
<ArrowsPointingOutIcon className="h-5 w-5" />
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<button
|
|
||||||
className="text-sm text-white font-medium bg-purple-600 hover:bg-purple-700 px-3 py-1 rounded-md"
|
|
||||||
onClick={toggleDrawer}
|
|
||||||
>
|
|
||||||
Create New
|
|
||||||
</button>
|
|
||||||
<div className={drawerClassName}>
|
|
||||||
<div className="sticky top-0 flex items-center justify-between p-4 bg-white border-b border-gray-200">
|
|
||||||
<h2 className="text-xl font-semibold text-gray-800">
|
|
||||||
Create New Item
|
|
||||||
</h2>
|
|
||||||
<div className="flex items-center space-x-2">
|
|
||||||
<button
|
|
||||||
onClick={toggleDrawerFullScreen}
|
|
||||||
className="p-2 text-gray-500 hover:text-gray-800 hover:bg-gray-100 rounded-lg"
|
|
||||||
>
|
|
||||||
{iconComponent}
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
onClick={toggleDrawer}
|
|
||||||
className="p-2 text-gray-500 hover:text-gray-800 hover:bg-gray-100 rounded-lg"
|
|
||||||
>
|
|
||||||
<ChevronDoubleLeftIcon className="h-5 w-5" />
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="p-6">
|
|
||||||
<div className="flex flex-col space-y-4">
|
|
||||||
{details.map((detail, index) => {
|
|
||||||
const value = newItemContent[detail.key] || "";
|
|
||||||
let inputField;
|
|
||||||
|
|
||||||
switch (detail.inputType) {
|
|
||||||
case "select-one":
|
|
||||||
initializeSelectField(detail.key);
|
|
||||||
inputField = (
|
|
||||||
<TagsInput
|
|
||||||
key={`${detail.key}-${renderKey}`}
|
|
||||||
presetValue={[]}
|
|
||||||
presetOptions={
|
|
||||||
detail.presetOptionsValues || []
|
|
||||||
}
|
|
||||||
setPresetOptions={
|
|
||||||
detail.presetOptionsSetter ||
|
|
||||||
(() => {})
|
|
||||||
}
|
|
||||||
singleValue={true}
|
|
||||||
onTagsChange={(
|
|
||||||
tags: Set<string>
|
|
||||||
) => {
|
|
||||||
setNewItemContent(
|
|
||||||
(prev: any) => ({
|
|
||||||
...prev,
|
|
||||||
[detail.key]:
|
|
||||||
tags.size > 0
|
|
||||||
? Array.from(
|
|
||||||
tags
|
|
||||||
)[0]
|
|
||||||
: null,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
case "select-multiple":
|
|
||||||
initializeSelectField(detail.key);
|
|
||||||
inputField = (
|
|
||||||
<TagsInput
|
|
||||||
key={`${detail.key}-${renderKey}`}
|
|
||||||
presetValue={
|
|
||||||
newItemContent[detail.key] || []
|
|
||||||
}
|
|
||||||
presetOptions={
|
|
||||||
detail.presetOptionsValues || []
|
|
||||||
}
|
|
||||||
setPresetOptions={
|
|
||||||
detail.presetOptionsSetter ||
|
|
||||||
(() => {})
|
|
||||||
}
|
|
||||||
onTagsChange={(
|
|
||||||
tags: Set<string>
|
|
||||||
) => {
|
|
||||||
setNewItemContent(
|
|
||||||
(prev: any) => ({
|
|
||||||
...prev,
|
|
||||||
[detail.key]:
|
|
||||||
Array.from(tags),
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
case "textarea":
|
|
||||||
inputField = (
|
|
||||||
<textarea
|
|
||||||
name={detail.key}
|
|
||||||
value={value}
|
|
||||||
onChange={handleContentChange}
|
|
||||||
rows={4}
|
|
||||||
onInput={(e) => {
|
|
||||||
const target =
|
|
||||||
e.target as HTMLTextAreaElement;
|
|
||||||
target.style.height = "auto";
|
|
||||||
target.style.height =
|
|
||||||
target.scrollHeight + "px";
|
|
||||||
}}
|
|
||||||
className="w-full p-2 focus:outline-none border border-gray-200 rounded-md resize-none font-normal"
|
|
||||||
placeholder={`Enter ${detail.label.toLowerCase()}...`}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
inputField = (
|
|
||||||
<input
|
|
||||||
type={detail.inputType}
|
|
||||||
name={detail.key}
|
|
||||||
value={value}
|
|
||||||
onChange={handleContentChange}
|
|
||||||
className="w-full p-2 border border-gray-200 rounded-md focus:outline-none focus:border-purple-500"
|
|
||||||
placeholder={`Enter ${detail.label.toLowerCase()}...`}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
key={index}
|
|
||||||
className="flex flex-col gap-2"
|
|
||||||
>
|
|
||||||
<label className="flex items-center text-sm text-gray-700 gap-2">
|
|
||||||
<span className="text-gray-500">
|
|
||||||
{detail.icon}
|
|
||||||
</span>
|
|
||||||
{detail.label}
|
|
||||||
</label>
|
|
||||||
{inputField}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
<div className="mt-6 flex justify-end">
|
|
||||||
<button
|
|
||||||
onClick={handleCreate}
|
|
||||||
className="px-4 py-2 bg-purple-600 text-white rounded-md hover:bg-purple-700"
|
|
||||||
>
|
|
||||||
Create
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default CreateDrawer;
|
|
|
@ -48,14 +48,12 @@ const Drawer: FunctionComponent<DrawerProps> = ({
|
||||||
const [isFavorite, setIsFavorite] = useState(false);
|
const [isFavorite, setIsFavorite] = useState(false);
|
||||||
const [tempRowContent, setTempRowContent] = useState(rowContent);
|
const [tempRowContent, setTempRowContent] = useState(rowContent);
|
||||||
|
|
||||||
const handleTempRowContentChangeHTML = (
|
const onRowUpdate = (updatedRow: any) => {};
|
||||||
|
|
||||||
|
const handleTempRowContentChange = (
|
||||||
e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>
|
e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>
|
||||||
) => {
|
) => {
|
||||||
const { name, value } = e.target;
|
const { name, value } = e.target;
|
||||||
handleTempRowContentChange(name, value);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleTempRowContentChange = (name: string, value: any) => {
|
|
||||||
setTempRowContent((prev: any) => ({
|
setTempRowContent((prev: any) => ({
|
||||||
...prev,
|
...prev,
|
||||||
[name]: value,
|
[name]: value,
|
||||||
|
@ -70,22 +68,10 @@ const Drawer: FunctionComponent<DrawerProps> = ({
|
||||||
};
|
};
|
||||||
|
|
||||||
const toggleDrawer = () => {
|
const toggleDrawer = () => {
|
||||||
if (setRowContent && isOpen) {
|
|
||||||
setRowContent((prev: any) => {
|
|
||||||
return prev.map((row: any) => {
|
|
||||||
if (row.id === tempRowContent.id) {
|
|
||||||
return tempRowContent;
|
|
||||||
}
|
|
||||||
return row;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
setIsOpen(!isOpen);
|
setIsOpen(!isOpen);
|
||||||
if (isFull) {
|
if (isFull) {
|
||||||
setIsFull(!isFull);
|
setIsFull(!isFull);
|
||||||
}
|
}
|
||||||
console.log("Send API request to update row content");
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const toggleDrawerFullScreen = () => setIsFull(!isFull);
|
const toggleDrawerFullScreen = () => setIsFull(!isFull);
|
||||||
|
@ -157,52 +143,16 @@ const Drawer: FunctionComponent<DrawerProps> = ({
|
||||||
|
|
||||||
switch (detail.inputType) {
|
switch (detail.inputType) {
|
||||||
case "select-one":
|
case "select-one":
|
||||||
valueToRender = (
|
|
||||||
<div className="flex-1">
|
|
||||||
<div className="rounded-md px-2 py-1">
|
|
||||||
<TagsInput
|
|
||||||
presetValue={
|
|
||||||
typeof value ===
|
|
||||||
"string"
|
|
||||||
? [value]
|
|
||||||
: value || []
|
|
||||||
}
|
|
||||||
presetOptions={
|
|
||||||
detail.presetOptionsValues ||
|
|
||||||
[]
|
|
||||||
}
|
|
||||||
setPresetOptions={
|
|
||||||
detail.presetOptionsSetter ||
|
|
||||||
(() => {})
|
|
||||||
}
|
|
||||||
singleValue={true}
|
|
||||||
onTagsChange={(
|
|
||||||
tags: Set<string>
|
|
||||||
) => {
|
|
||||||
const tagsArray =
|
|
||||||
Array.from(tags);
|
|
||||||
handleTempRowContentChange(
|
|
||||||
detail.key,
|
|
||||||
tagsArray.length > 0
|
|
||||||
? tagsArray[0]
|
|
||||||
: null
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
case "select-multiple":
|
case "select-multiple":
|
||||||
valueToRender = (
|
valueToRender = (
|
||||||
<div className="flex-1">
|
<div className="flex-1">
|
||||||
<div className="rounded-md px-2 py-1">
|
<div className="hover:bg-gray-50 rounded-md px-2 py-1">
|
||||||
<TagsInput
|
<TagsInput
|
||||||
presetValue={
|
presetValue={
|
||||||
typeof value ===
|
typeof value ===
|
||||||
"string"
|
"string"
|
||||||
? [value]
|
? [value]
|
||||||
: value || []
|
: value
|
||||||
}
|
}
|
||||||
presetOptions={
|
presetOptions={
|
||||||
detail.presetOptionsValues ||
|
detail.presetOptionsValues ||
|
||||||
|
@ -212,14 +162,6 @@ const Drawer: FunctionComponent<DrawerProps> = ({
|
||||||
detail.presetOptionsSetter ||
|
detail.presetOptionsSetter ||
|
||||||
(() => {})
|
(() => {})
|
||||||
}
|
}
|
||||||
onTagsChange={(
|
|
||||||
tags: Set<string>
|
|
||||||
) => {
|
|
||||||
handleTempRowContentChange(
|
|
||||||
detail.key,
|
|
||||||
Array.from(tags)
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -233,7 +175,7 @@ const Drawer: FunctionComponent<DrawerProps> = ({
|
||||||
name={detail.key}
|
name={detail.key}
|
||||||
value={value}
|
value={value}
|
||||||
onChange={
|
onChange={
|
||||||
handleTempRowContentChangeHTML
|
handleTempRowContentChange
|
||||||
}
|
}
|
||||||
onKeyDown={handleEnterPress}
|
onKeyDown={handleEnterPress}
|
||||||
rows={4}
|
rows={4}
|
||||||
|
@ -261,7 +203,7 @@ const Drawer: FunctionComponent<DrawerProps> = ({
|
||||||
name={detail.key}
|
name={detail.key}
|
||||||
value={value}
|
value={value}
|
||||||
onChange={
|
onChange={
|
||||||
handleTempRowContentChangeHTML
|
handleTempRowContentChange
|
||||||
}
|
}
|
||||||
onKeyDown={handleEnterPress}
|
onKeyDown={handleEnterPress}
|
||||||
className="w-full p-1 focus:outline-gray-200 bg-transparent"
|
className="w-full p-1 focus:outline-gray-200 bg-transparent"
|
||||||
|
@ -279,7 +221,7 @@ const Drawer: FunctionComponent<DrawerProps> = ({
|
||||||
name={detail.key}
|
name={detail.key}
|
||||||
value={value}
|
value={value}
|
||||||
onChange={
|
onChange={
|
||||||
handleTempRowContentChangeHTML
|
handleTempRowContentChange
|
||||||
}
|
}
|
||||||
onKeyDown={handleEnterPress}
|
onKeyDown={handleEnterPress}
|
||||||
className="w-full p-1 font-normal hover:text-gray-400 focus:outline-gray-200 underline text-gray-500 bg-transparent"
|
className="w-full p-1 font-normal hover:text-gray-400 focus:outline-gray-200 underline text-gray-500 bg-transparent"
|
||||||
|
|
|
@ -33,7 +33,7 @@ export const FilterBox = () => {
|
||||||
>
|
>
|
||||||
<span>{tag}</span>
|
<span>{tag}</span>
|
||||||
<span
|
<span
|
||||||
className="cursor-pointer"
|
className="ml-2 cursor-pointer"
|
||||||
onClick={() => handleTagChange(tag)}
|
onClick={() => handleTagChange(tag)}
|
||||||
>
|
>
|
||||||
×
|
×
|
||||||
|
|
|
@ -106,11 +106,7 @@ export default function ResourceTable({ data, setData }: ResourceTableProps) {
|
||||||
),
|
),
|
||||||
cell: (info) => (
|
cell: (info) => (
|
||||||
<div className="flex flex-wrap gap-2 items-center px-2">
|
<div className="flex flex-wrap gap-2 items-center px-2">
|
||||||
<Tag>
|
<Tag>{info.getValue()}</Tag>
|
||||||
{info.getValue().length != 0
|
|
||||||
? info.getValue()
|
|
||||||
: "no program"}
|
|
||||||
</Tag>
|
|
||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
}),
|
}),
|
||||||
|
@ -131,12 +127,5 @@ export default function ResourceTable({ data, setData }: ResourceTableProps) {
|
||||||
}),
|
}),
|
||||||
];
|
];
|
||||||
|
|
||||||
return (
|
return <Table data={data} setData={setData} columns={columns} />;
|
||||||
<Table
|
|
||||||
data={data}
|
|
||||||
setData={setData}
|
|
||||||
columns={columns}
|
|
||||||
details={resourceDetails}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -115,11 +115,7 @@ export default function ServiceTable({ data, setData }: ServiceTableProps) {
|
||||||
),
|
),
|
||||||
cell: (info) => (
|
cell: (info) => (
|
||||||
<div className="flex flex-wrap gap-2 items-center px-2">
|
<div className="flex flex-wrap gap-2 items-center px-2">
|
||||||
<Tag>
|
<Tag>{info.getValue()}</Tag>
|
||||||
{info.getValue().length != 0
|
|
||||||
? info.getValue()
|
|
||||||
: "no program"}
|
|
||||||
</Tag>
|
|
||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
}),
|
}),
|
||||||
|
@ -132,13 +128,9 @@ export default function ServiceTable({ data, setData }: ServiceTableProps) {
|
||||||
),
|
),
|
||||||
cell: (info) => (
|
cell: (info) => (
|
||||||
<div className="flex flex-wrap gap-2 items-center px-2">
|
<div className="flex flex-wrap gap-2 items-center px-2">
|
||||||
{info.getValue().length > 0 ? (
|
{info.getValue().map((tag: string, index: number) => {
|
||||||
info.getValue().map((tag: string, index: number) => {
|
return <Tag key={index}>{tag}</Tag>;
|
||||||
return <Tag key={index}>{tag}</Tag>;
|
})}
|
||||||
})
|
|
||||||
) : (
|
|
||||||
<Tag>no requirements</Tag>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
}),
|
}),
|
||||||
|
@ -159,12 +151,5 @@ export default function ServiceTable({ data, setData }: ServiceTableProps) {
|
||||||
}),
|
}),
|
||||||
];
|
];
|
||||||
|
|
||||||
return (
|
return <Table data={data} setData={setData} columns={columns} />;
|
||||||
<Table
|
|
||||||
data={data}
|
|
||||||
setData={setData}
|
|
||||||
columns={columns}
|
|
||||||
details={serviceDetails}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,33 +19,11 @@ import { PlusIcon } from "@heroicons/react/24/solid";
|
||||||
import { rankItem } from "@tanstack/match-sorter-utils";
|
import { rankItem } from "@tanstack/match-sorter-utils";
|
||||||
import { RowOptionMenu } from "./RowOptionMenu";
|
import { RowOptionMenu } from "./RowOptionMenu";
|
||||||
import DataPoint from "@/utils/models/DataPoint";
|
import DataPoint from "@/utils/models/DataPoint";
|
||||||
import CreateDrawer from "../Drawer/CreateDrawer";
|
|
||||||
import { Details } from "../Drawer/Drawer";
|
|
||||||
|
|
||||||
type TableProps<T extends DataPoint> = {
|
type TableProps<T extends DataPoint> = {
|
||||||
data: T[];
|
data: T[];
|
||||||
setData: Dispatch<SetStateAction<T[]>>;
|
setData: Dispatch<SetStateAction<T[]>>;
|
||||||
columns: ColumnDef<T, any>[];
|
columns: ColumnDef<T, any>[];
|
||||||
details: Details[];
|
|
||||||
};
|
|
||||||
|
|
||||||
/** Validates that all required fields in a new item have values */
|
|
||||||
const validateNewItem = (newItem: any, details: Details[]): boolean => {
|
|
||||||
const hasEmptyFields = details.some((detail) => {
|
|
||||||
const value = newItem[detail.key];
|
|
||||||
return (
|
|
||||||
value === undefined ||
|
|
||||||
value === null ||
|
|
||||||
value === "" ||
|
|
||||||
(Array.isArray(value) && value.length === 0)
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (hasEmptyFields) {
|
|
||||||
alert("Please fill in all fields before creating a new item");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Fuzzy search function */
|
/** Fuzzy search function */
|
||||||
|
@ -75,43 +53,46 @@ export default function Table<T extends DataPoint>({
|
||||||
data,
|
data,
|
||||||
setData,
|
setData,
|
||||||
columns,
|
columns,
|
||||||
details,
|
|
||||||
}: TableProps<T>) {
|
}: TableProps<T>) {
|
||||||
const columnHelper = createColumnHelper<T>();
|
const columnHelper = createColumnHelper<T>();
|
||||||
|
|
||||||
// /** Sorting function based on visibility */
|
/** Sorting function based on visibility */
|
||||||
// const visibilitySort = (a: T, b: T) =>
|
const visibilitySort = (a: T, b: T) =>
|
||||||
// a.visible === b.visible ? 0 : a.visible ? -1 : 1;
|
a.visible === b.visible ? 0 : a.visible ? -1 : 1;
|
||||||
|
|
||||||
// // Sort data on load
|
// Sort data on load
|
||||||
// useEffect(() => {
|
useEffect(() => {
|
||||||
// setData((prevData) => prevData.sort(visibilitySort));
|
setData((prevData) => prevData.sort(visibilitySort));
|
||||||
// }, [setData]);
|
}, [setData]);
|
||||||
|
|
||||||
// // Data manipulation methods
|
// Data manipulation methods
|
||||||
// // TODO: Connect data manipulation methods to the database (deleteData, hideData, addData)
|
// TODO: Connect data manipulation methods to the database (deleteData, hideData, addData)
|
||||||
// const deleteData = (dataId: number) => {
|
const deleteData = (dataId: number) => {
|
||||||
// console.log(data);
|
console.log(data);
|
||||||
// setData((currentData) =>
|
setData((currentData) =>
|
||||||
// currentData.filter((data) => data.id !== dataId)
|
currentData.filter((data) => data.id !== dataId)
|
||||||
// );
|
);
|
||||||
// };
|
};
|
||||||
|
|
||||||
// const hideData = (dataId: number) => {
|
const hideData = (dataId: number) => {
|
||||||
// console.log(`Toggling visibility for data with ID: ${dataId}`);
|
console.log(`Toggling visibility for data with ID: ${dataId}`);
|
||||||
// setData((currentData) => {
|
setData((currentData) => {
|
||||||
// const newData = currentData
|
const newData = currentData
|
||||||
// .map((data) =>
|
.map((data) =>
|
||||||
// data.id === dataId
|
data.id === dataId
|
||||||
// ? { ...data, visible: !data.visible }
|
? { ...data, visible: !data.visible }
|
||||||
// : data
|
: data
|
||||||
// )
|
)
|
||||||
// .sort(visibilitySort);
|
.sort(visibilitySort);
|
||||||
|
|
||||||
// console.log(newData);
|
console.log(newData);
|
||||||
// return newData;
|
return newData;
|
||||||
// });
|
});
|
||||||
// };
|
};
|
||||||
|
|
||||||
|
const addData = () => {
|
||||||
|
setData([...data]);
|
||||||
|
};
|
||||||
|
|
||||||
// Add data manipulation options to the first column
|
// Add data manipulation options to the first column
|
||||||
columns.unshift(
|
columns.unshift(
|
||||||
|
@ -119,10 +100,8 @@ export default function Table<T extends DataPoint>({
|
||||||
id: "options",
|
id: "options",
|
||||||
cell: (props) => (
|
cell: (props) => (
|
||||||
<RowOptionMenu
|
<RowOptionMenu
|
||||||
onDelete={() => {}}
|
onDelete={() => deleteData(props.row.original.id)}
|
||||||
onHide={() => {}}
|
onHide={() => hideData(props.row.original.id)}
|
||||||
// onDelete={() => deleteData(props.row.original.id)}
|
|
||||||
// onHide={() => hideData(props.row.original.id)}
|
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
})
|
})
|
||||||
|
@ -135,6 +114,11 @@ export default function Table<T extends DataPoint>({
|
||||||
setQuery(String(target.value));
|
setQuery(String(target.value));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleCellChange = (e: ChangeEvent, key: Key) => {
|
||||||
|
const target = e.target as HTMLInputElement;
|
||||||
|
console.log(key);
|
||||||
|
};
|
||||||
|
|
||||||
// TODO: Filtering
|
// TODO: Filtering
|
||||||
|
|
||||||
// TODO: Sorting
|
// TODO: Sorting
|
||||||
|
@ -154,6 +138,16 @@ export default function Table<T extends DataPoint>({
|
||||||
getCoreRowModel: getCoreRowModel(),
|
getCoreRowModel: getCoreRowModel(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const handleRowData = (row: any) => {
|
||||||
|
const rowData: any = {};
|
||||||
|
row.cells.forEach((cell: any) => {
|
||||||
|
rowData[cell.column.id] = cell.value;
|
||||||
|
});
|
||||||
|
// Use rowData object containing data from all columns for the current row
|
||||||
|
console.log(rowData);
|
||||||
|
return rowData;
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col">
|
<div className="flex flex-col">
|
||||||
<div className="flex flex-row justify-end">
|
<div className="flex flex-row justify-end">
|
||||||
|
@ -214,21 +208,14 @@ export default function Table<T extends DataPoint>({
|
||||||
<tfoot>
|
<tfoot>
|
||||||
<tr>
|
<tr>
|
||||||
<td
|
<td
|
||||||
className="p-3 border-y border-gray-200"
|
className="p-3 border-y border-gray-200 text-gray-600 hover:bg-gray-50"
|
||||||
colSpan={100}
|
colSpan={100}
|
||||||
|
onClick={addData}
|
||||||
>
|
>
|
||||||
<CreateDrawer
|
<span className="flex ml-1 text-gray-500">
|
||||||
details={details}
|
<PlusIcon className="inline h-4 mr-1" />
|
||||||
onCreate={(newItem) => {
|
New
|
||||||
if (!validateNewItem(newItem, details)) {
|
</span>
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
newItem.visible = true;
|
|
||||||
setData((prev) => [...prev, newItem]);
|
|
||||||
return true;
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tfoot>
|
</tfoot>
|
||||||
|
|
|
@ -97,12 +97,8 @@ export default function UserTable({ data, setData }: UserTableProps) {
|
||||||
</>
|
</>
|
||||||
),
|
),
|
||||||
cell: (info) => (
|
cell: (info) => (
|
||||||
<div className="flex flex-wrap gap-2 items-center px-2">
|
<div className="flex ml-2 flex-wrap gap-2 items-center">
|
||||||
<Tag>
|
<Tag>{info.getValue()}</Tag>
|
||||||
{info.getValue() && info.getValue().length != 0
|
|
||||||
? info.getValue()
|
|
||||||
: "no role"}
|
|
||||||
</Tag>
|
|
||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
}),
|
}),
|
||||||
|
@ -126,24 +122,13 @@ export default function UserTable({ data, setData }: UserTableProps) {
|
||||||
),
|
),
|
||||||
cell: (info) => (
|
cell: (info) => (
|
||||||
<div className="flex ml-2 flex-wrap gap-2 items-center">
|
<div className="flex ml-2 flex-wrap gap-2 items-center">
|
||||||
{info.getValue().length > 0 ? (
|
{info.getValue().map((tag: string, index: number) => {
|
||||||
info.getValue().map((tag: string, index: number) => {
|
return <Tag key={index}>{tag}</Tag>;
|
||||||
return <Tag key={index}>{tag}</Tag>;
|
})}
|
||||||
})
|
|
||||||
) : (
|
|
||||||
<Tag>no programs</Tag>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
}),
|
}),
|
||||||
];
|
];
|
||||||
|
|
||||||
return (
|
return <Table<User> data={data} setData={setData} columns={columns} />;
|
||||||
<Table<User>
|
|
||||||
data={data}
|
|
||||||
setData={setData}
|
|
||||||
columns={columns}
|
|
||||||
details={userDetails}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,16 +8,12 @@ interface TagsInputProps {
|
||||||
presetOptions: string[];
|
presetOptions: string[];
|
||||||
presetValue: string[];
|
presetValue: string[];
|
||||||
setPresetOptions: Dispatch<SetStateAction<string[]>>;
|
setPresetOptions: Dispatch<SetStateAction<string[]>>;
|
||||||
onTagsChange?: (tags: Set<string>) => void;
|
|
||||||
singleValue?: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const TagsInput: React.FC<TagsInputProps> = ({
|
const TagsInput: React.FC<TagsInputProps> = ({
|
||||||
presetValue,
|
presetValue,
|
||||||
presetOptions,
|
presetOptions,
|
||||||
setPresetOptions,
|
setPresetOptions,
|
||||||
onTagsChange,
|
|
||||||
singleValue = false,
|
|
||||||
}) => {
|
}) => {
|
||||||
const [inputValue, setInputValue] = useState("");
|
const [inputValue, setInputValue] = useState("");
|
||||||
const [cellSelected, setCellSelected] = useState(false);
|
const [cellSelected, setCellSelected] = useState(false);
|
||||||
|
@ -67,10 +63,6 @@ const TagsInput: React.FC<TagsInputProps> = ({
|
||||||
|
|
||||||
const handleAddTag = (e: React.KeyboardEvent<HTMLInputElement>) => {
|
const handleAddTag = (e: React.KeyboardEvent<HTMLInputElement>) => {
|
||||||
if (e.key === "Enter" && inputValue.trim()) {
|
if (e.key === "Enter" && inputValue.trim()) {
|
||||||
if (singleValue && tags.size >= 1) {
|
|
||||||
// Don't add new tag if we're in single value mode and already have a tag
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
addTag(e);
|
addTag(e);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -78,32 +70,23 @@ const TagsInput: React.FC<TagsInputProps> = ({
|
||||||
const addTag = (e?: React.KeyboardEvent<HTMLInputElement>) => {
|
const addTag = (e?: React.KeyboardEvent<HTMLInputElement>) => {
|
||||||
e?.stopPropagation();
|
e?.stopPropagation();
|
||||||
|
|
||||||
const newTags = new Set(Array.from(tags).concat(inputValue));
|
|
||||||
setOptions(new Set(Array.from(options).concat(inputValue)));
|
setOptions(new Set(Array.from(options).concat(inputValue)));
|
||||||
setTags(newTags);
|
setTags(new Set(Array.from(tags).concat(inputValue)));
|
||||||
setFilteredOptions(new Set(Array.from(options).concat(inputValue)));
|
setFilteredOptions(new Set(Array.from(options).concat(inputValue)));
|
||||||
setInputValue("");
|
setInputValue("");
|
||||||
onTagsChange?.(newTags);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSelectTag = (tagToAdd: string) => {
|
const handleSelectTag = (tagToAdd: string) => {
|
||||||
if (singleValue) {
|
console.log(tagToAdd);
|
||||||
const newTags = new Set([tagToAdd]);
|
console.log(tags);
|
||||||
setTags(newTags);
|
|
||||||
onTagsChange?.(newTags);
|
if (!tags.has(tagToAdd)) {
|
||||||
} else if (!tags.has(tagToAdd)) {
|
setTags(new Set(Array.from(tags).concat(tagToAdd)));
|
||||||
const newTags = new Set(Array.from(tags).concat(tagToAdd));
|
|
||||||
setTags(newTags);
|
|
||||||
onTagsChange?.(newTags);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleDeleteTag = (tagToDelete: string) => {
|
const handleDeleteTag = (tagToDelete: string) => {
|
||||||
const newTags = new Set(
|
setTags(new Set(Array.from(tags).filter((tag) => tag !== tagToDelete)));
|
||||||
Array.from(tags).filter((tag) => tag !== tagToDelete)
|
|
||||||
);
|
|
||||||
setTags(newTags);
|
|
||||||
onTagsChange?.(newTags);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleDeleteTagOption = (tagToDelete: string) => {
|
const handleDeleteTagOption = (tagToDelete: string) => {
|
||||||
|
@ -161,45 +144,31 @@ const TagsInput: React.FC<TagsInputProps> = ({
|
||||||
active
|
active
|
||||||
tags={tags}
|
tags={tags}
|
||||||
/>
|
/>
|
||||||
{(!singleValue || tags.size === 0) && (
|
<input
|
||||||
<input
|
type="text"
|
||||||
type="text"
|
value={inputValue}
|
||||||
value={inputValue}
|
placeholder="Search for an option..."
|
||||||
placeholder={
|
onChange={handleInputChange}
|
||||||
singleValue && tags.size > 0
|
onKeyDown={handleAddTag}
|
||||||
? ""
|
className="focus:outline-none bg-transparent"
|
||||||
: "Search for an option..."
|
autoFocus
|
||||||
}
|
/>
|
||||||
onChange={handleInputChange}
|
|
||||||
onKeyDown={handleAddTag}
|
|
||||||
className="focus:outline-none bg-transparent"
|
|
||||||
autoFocus
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
<div className="flex rounded-b-md bg-white flex-col border-t border-gray-100 text-2xs font-medium text-gray-500 p-2">
|
<div className="flex rounded-b-md bg-white flex-col border-t border-gray-100 text-2xs font-medium text-gray-500 p-2">
|
||||||
<p className="capitalize">
|
<p className="capitalize">
|
||||||
{singleValue && tags.size > 0
|
Select an option or create one
|
||||||
? "Only one option can be selected"
|
|
||||||
: "Select an option or create one"}
|
|
||||||
</p>
|
</p>
|
||||||
{(!singleValue || tags.size === 0) && (
|
<TagDropdown
|
||||||
<>
|
handleDeleteTag={handleDeleteTagOption}
|
||||||
<TagDropdown
|
handleEditTag={handleEditTag}
|
||||||
handleDeleteTag={
|
handleAdd={handleSelectTag}
|
||||||
handleDeleteTagOption
|
tags={filteredOptions}
|
||||||
}
|
/>
|
||||||
handleEditTag={handleEditTag}
|
{inputValue.length > 0 && (
|
||||||
handleAdd={handleSelectTag}
|
<CreateNewTagAction
|
||||||
tags={filteredOptions}
|
input={inputValue}
|
||||||
/>
|
addTag={addTag}
|
||||||
{inputValue.length > 0 && (
|
/>
|
||||||
<CreateNewTagAction
|
|
||||||
input={inputValue}
|
|
||||||
addTag={addTag}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -16,27 +16,21 @@ export const TagDropdown = ({
|
||||||
}: TagDropdownProps) => {
|
}: TagDropdownProps) => {
|
||||||
return (
|
return (
|
||||||
<div className="z-50 flex flex-col space-y-2 mt-2">
|
<div className="z-50 flex flex-col space-y-2 mt-2">
|
||||||
{Array.from(tags).length > 0 ? (
|
{Array.from(tags).map((tag, index) => (
|
||||||
Array.from(tags).map((tag, index) => (
|
<div
|
||||||
<div
|
key={index}
|
||||||
key={index}
|
className="items-center rounded-md p-1 flex flex-row justify-between hover:bg-gray-100"
|
||||||
className="items-center rounded-md p-1 flex flex-row justify-between hover:bg-gray-100"
|
>
|
||||||
>
|
<button onClick={() => handleAdd(tag)}>
|
||||||
<button onClick={() => handleAdd(tag)}>
|
<Tag>{tag}</Tag>
|
||||||
<Tag>{tag}</Tag>
|
</button>
|
||||||
</button>
|
<DropdownAction
|
||||||
<DropdownAction
|
handleDeleteTag={handleDeleteTag}
|
||||||
handleDeleteTag={handleDeleteTag}
|
handleEditTag={handleEditTag}
|
||||||
handleEditTag={handleEditTag}
|
tag={tag}
|
||||||
tag={tag}
|
/>
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
))
|
|
||||||
) : (
|
|
||||||
<div className="text-gray-500 text-sm p-1">
|
|
||||||
No options available. Type to create new ones.
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
))}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -7,25 +7,21 @@ export interface Tags {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const TagsArray = ({ tags, handleDelete, active = false }: Tags) => {
|
export const TagsArray = ({ tags, handleDelete, active = false }: Tags) => {
|
||||||
|
// console.log(tags);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-wrap gap-2 items-center min-h-[24px] min-w-[100px] rounded-md hover:bg-gray-100 p-1">
|
<div className="flex ml-2 flex-wrap gap-2 items-center">
|
||||||
{Array.from(tags).length > 0 ? (
|
{Array.from(tags).map((tag, index) => {
|
||||||
Array.from(tags).map((tag, index) => {
|
return (
|
||||||
return (
|
<Tag
|
||||||
<Tag
|
handleDelete={handleDelete}
|
||||||
handleDelete={handleDelete}
|
active={active}
|
||||||
active={active}
|
key={index}
|
||||||
key={index}
|
>
|
||||||
>
|
{tag}
|
||||||
{tag}
|
</Tag>
|
||||||
</Tag>
|
);
|
||||||
);
|
})}
|
||||||
})
|
|
||||||
) : (
|
|
||||||
<span className="text-gray-400 text-sm cursor-pointer">
|
|
||||||
Click to select tags
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue
Block a user