diff --git a/.storybook/main.ts b/.storybook/main.ts index a8ec963..e1c1d40 100644 --- a/.storybook/main.ts +++ b/.storybook/main.ts @@ -1,20 +1,15 @@ import type { StorybookConfig } from "@storybook/react-vite"; +import path from "path"; + const config: StorybookConfig = { - stories: [ - "../stories/**/*.mdx", - "../stories/**/*.stories.@(js|jsx|ts|tsx)", - "../app/components/**/*.stories.@(jsx|tsx)", - ], + stories: ["../app/components/**/*.stories.@(js|ts|jsx|tsx)"], addons: [ "@storybook/addon-links", "@storybook/addon-essentials", "@storybook/addon-interactions", - { - name: "@storybook/addon-styling", - options: { - postCss: true, - }, - }, + "@storybook/addon-actions", + "@storybook/addon-styling", + "@storybook/addon-console", ], framework: { name: "@storybook/react-vite", @@ -23,5 +18,21 @@ const config: StorybookConfig = { docs: { autodocs: "tag", }, + viteFinal: async (config) => { + return { + ...config, + define: { + "process.env": process.env, + }, + resolve: { + alias: [ + { + find: "~", + replacement: path.resolve(__dirname, "../app"), + }, + ], + }, + }; + }, }; export default config; diff --git a/.storybook/preview.ts b/.storybook/preview.ts index 98f39de..7df8d05 100644 --- a/.storybook/preview.ts +++ b/.storybook/preview.ts @@ -1,5 +1,7 @@ import type { Preview } from "@storybook/react"; -import '../app/styles/tailwind.css'; +import "../app/styles/tailwind.css"; +import "@storybook/addon-console"; +import { withConsole } from "@storybook/addon-console"; const preview: Preview = { parameters: { @@ -11,6 +13,7 @@ const preview: Preview = { }, }, }, + decorators: [(storyFn, context) => withConsole()(storyFn)(context)], }; export default preview; diff --git a/app/components/Comments/Comment/Comment.tsx b/app/components/Comments/Comment/Comment.tsx index 5e18e1b..59128e0 100644 --- a/app/components/Comments/Comment/Comment.tsx +++ b/app/components/Comments/Comment/Comment.tsx @@ -1,7 +1,6 @@ import type { user } from "@prisma/client"; import { useFetcher } from "@remix-run/react"; -import React, { ChangeEvent, useEffect, useRef, useState } from "react"; -import LikeForm from "~/components/Form/LikeForm"; +import React, { useEffect, useRef, useState } from "react"; import EditIcon from "~/components/Icons/EditIcon"; import HeartIcon from "~/components/Icons/HeartIcon"; import ReplyIcon from "~/components/Icons/ReplyIcon"; diff --git a/app/components/Form/BottleForm/BottleForm.tsx b/app/components/Form/BottleForm/BottleForm.tsx index c149017..5e3c035 100644 --- a/app/components/Form/BottleForm/BottleForm.tsx +++ b/app/components/Form/BottleForm/BottleForm.tsx @@ -58,7 +58,7 @@ export default function BottleForm({ imageIsSubmitting={imageIsSubmitting} />
-
+
{submissionSuccessful ? (
-
+
@@ -128,7 +128,7 @@ export default function ConfirmForm({ data }: ConfirmFormProps) {
Edit Your Review @@ -197,7 +197,7 @@ export default function ConfirmForm({ data }: ConfirmFormProps) {
Edit Tasting Notes diff --git a/app/components/Form/LoginForm/LoginForm.tsx b/app/components/Form/LoginForm/LoginForm.tsx new file mode 100644 index 0000000..c900d86 --- /dev/null +++ b/app/components/Form/LoginForm/LoginForm.tsx @@ -0,0 +1,120 @@ +import { type FieldConfig, conform } from "@conform-to/react"; +import { Form, Link, useNavigation, useSearchParams } from "@remix-run/react"; +import type { Form as ConformForm } from "~/utils/types"; + +type LoginFormProps = { + accountIdentifier: FieldConfig; + password: FieldConfig; + redirectTo?: string; + form: ConformForm; +}; + +export default function LoginForm({ + accountIdentifier, + password, + redirectTo, + form, +}: LoginFormProps) { + const navigation = useNavigation(); + const [searchParams] = useSearchParams(); + + return ( + +
+ +
+ + {accountIdentifier.error ? ( + + ) : null} +
+
+ +
+ +
+ + {password.error ? ( + + ) : null} +
+
+ + + +
+
+ + +
+
+ Don't have an account?{" "} + + Sign up + +
+
+ + ); +} diff --git a/app/components/Form/LoginForm/index.ts b/app/components/Form/LoginForm/index.ts new file mode 100644 index 0000000..17a2b47 --- /dev/null +++ b/app/components/Form/LoginForm/index.ts @@ -0,0 +1,3 @@ +import LoginForm from "./LoginForm"; + +export default LoginForm; diff --git a/app/components/Form/RegisterForm/RegisterForm.stories.tsx b/app/components/Form/RegisterForm/RegisterForm.stories.tsx new file mode 100644 index 0000000..359669a --- /dev/null +++ b/app/components/Form/RegisterForm/RegisterForm.stories.tsx @@ -0,0 +1,58 @@ +import { unstable_createRemixStub as createRemixStub } from "@remix-run/testing"; +import type { Meta, StoryObj } from "@storybook/react"; +import RegisterForm from "./RegisterForm"; + +const meta: Meta = { + title: "components/Form/RegisterForm", + component: RegisterForm, + decorators: [ + (Story) => { + const RemixStub = createRemixStub([{ path: "/", element: }]); + return ; + }, + ], + tags: ["autodocs"], + args: { + email: { + id: "email", + name: "email", + errorId: "email-error", + defaultValue: "jpeckiii@gmail.com", + }, + username: { + id: "username", + name: "username", + errorId: "username-error", + defaultValue: "petereck123", + }, + password: { + id: "password", + name: "password", + errorId: "password-error", + defaultValue: "password123", + }, + confirmPassword: { + id: "email", + name: "email", + errorId: "email-error", + defaultValue: "password123", + }, + }, +}; + +export default meta; + +type Story = StoryObj; + +export const Primary: Story = { + render: () => ( + + ), +}; diff --git a/app/components/Form/RegisterForm/RegisterForm.tsx b/app/components/Form/RegisterForm/RegisterForm.tsx new file mode 100644 index 0000000..89415ea --- /dev/null +++ b/app/components/Form/RegisterForm/RegisterForm.tsx @@ -0,0 +1,167 @@ +import { type FieldConfig } from "@conform-to/react"; +import { Form, Link, useNavigation } from "@remix-run/react"; +import { conform } from "@conform-to/react"; +import type { URLSearchParams } from "url"; +import type { Form as ConformForm } from "~/utils/types"; + +type RegisterFormProps = { + email: FieldConfig; + username: FieldConfig; + password: FieldConfig; + confirmPassword: FieldConfig; + searchParams: URLSearchParams; + form: ConformForm; +}; + +export default function RegisterForm({ + searchParams, + email, + username, + password, + confirmPassword, + form, +}: RegisterFormProps) { + const navigation = useNavigation(); + const redirectTo = searchParams.get("redirectTo"); + + return ( +
+
+ +
+ + {email.error ? ( + + ) : null} +
+
+ +
+ +
+ + {username.error ? ( + + ) : null} +
+
+ +
+ +
+ + {password.error ? ( + + ) : null} +
+
+ +
+ +
+ + {confirmPassword.error ? ( + + ) : null} +
+
+ + + +
+
+ Already have an account?{" "} + + Log in + +
+
+
+ ); +} diff --git a/app/components/Form/RegisterForm/index.ts b/app/components/Form/RegisterForm/index.ts new file mode 100644 index 0000000..86a3812 --- /dev/null +++ b/app/components/Form/RegisterForm/index.ts @@ -0,0 +1,3 @@ +import RegisterForm from "./RegisterForm"; + +export default RegisterForm; diff --git a/app/components/Grids/BottleTable/BottleTable.tsx b/app/components/Grids/BottleTable/BottleTable.tsx deleted file mode 100644 index 2247038..0000000 --- a/app/components/Grids/BottleTable/BottleTable.tsx +++ /dev/null @@ -1,64 +0,0 @@ -import type { GridBottle } from "~/models/bottle.server"; -import type { Column } from "~/routes/services/search/bottle/fetch"; -import Caption from "../Common/Caption"; -import TableBody from "../Common/TableBody/TableBody"; -import Thead from "../Common/Thead"; -import type { SortDirection, SortFields } from "../TestGrid/TestGrid"; -import { useCallback, useEffect, useRef } from "react"; - -type BottleTableProps = { - columns: Column[]; - items: GridBottle[] | []; - totalItems: number; - sortField: SortFields; - sortDirection: SortDirection; - tableHeight: string | number; - activeIndex: number | null; - handleSortingChange: (field: SortFields) => void; - mouseDown: (index: number) => void; -}; - -export default function BottleTable({ - columns, - items, - totalItems, - sortField, - sortDirection, - tableHeight, - activeIndex, - mouseDown, - handleSortingChange, -}: BottleTableProps) { - return ( -
-
- - - -
-
-
-
- ); -} diff --git a/app/components/Grids/BottleTable/index.ts b/app/components/Grids/BottleTable/index.ts deleted file mode 100644 index f735095..0000000 --- a/app/components/Grids/BottleTable/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -import BottleTable from "./BottleTable"; - -export default BottleTable; diff --git a/app/components/Grids/Common/Cell/Cell.tsx b/app/components/Grids/Common/Cell/Cell.tsx deleted file mode 100644 index 2f1cc31..0000000 --- a/app/components/Grids/Common/Cell/Cell.tsx +++ /dev/null @@ -1,8 +0,0 @@ -type CellProps = { - rowIndex: number; - value: string | number; -}; - -export default function Cell({ value, rowIndex }: CellProps) { - return
{value}
; -} diff --git a/app/components/Grids/Common/Cell/index.ts b/app/components/Grids/Common/Cell/index.ts deleted file mode 100644 index 0a22bd6..0000000 --- a/app/components/Grids/Common/Cell/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -import Cell from "./Cell"; - -export default Cell; diff --git a/app/components/Grids/Common/HeadCell/HeadCell.tsx b/app/components/Grids/Common/HeadCell/HeadCell.tsx deleted file mode 100644 index 5513ba2..0000000 --- a/app/components/Grids/Common/HeadCell/HeadCell.tsx +++ /dev/null @@ -1,103 +0,0 @@ -import FilterArrowDown from "~/components/Icons/FilterArrowDown"; -import FilterArrows from "~/components/Icons/FilterArrows"; -import FilterArrowUp from "~/components/Icons/FilterArrowUp"; -import type { Column } from "~/routes/services/search/bottle/fetch"; -import type { SortDirection, SortFields } from "../../TestGrid/TestGrid"; - -type HeadCellProps = { - column: Column; - sortField: SortFields; - sortDirection: SortDirection; - tableHeight: string | number; - key: string | number; - index: number; - activeIndex: number | null; - handleSortingChange: (sortField: SortFields) => void; - mouseDown: (index: number) => void; -}; - -export default function HeadCell({ - column, - key, - index, - activeIndex, - sortField, - sortDirection, - tableHeight, - mouseDown, - handleSortingChange, -}: HeadCellProps) { - return ( - <> - {column.sort ? ( - handleSortingChange(column.field as SortFields)} - className={ - column.textPosition - ? column.textPosition + - " border-b-2 text-gray-700 first-of-type:sticky first-of-type:left-0 first-of-type:w-[300px] first-of-type:min-w-[300px] first-of-type:bg-white" - : "border-b-2 text-left text-gray-700 first-of-type:sticky first-of-type:left-0 first-of-type:w-[300px] first-of-type:min-w-[300px] first-of-type:bg-white" - } - > -
-
{column.header}
-
- {column.sort && - column.field === sortField && - sortDirection === "asc" ? ( - - ) : column.field === sortField && sortDirection === "desc" ? ( - - ) : ( - - )} -
-
mouseDown(index)} - className={`z-1 absolute right-0 top-0 block w-2 cursor-col-resize border-r-2 border-transparent hover:border-gray-200 ${ - activeIndex === index ? "border-gray-700" : "" - }`} - /> -
- - ) : ( - - {column.header} -
mouseDown(index)} - className={`z-1 absolute right-0 top-0 block w-2 cursor-col-resize border-r-2 border-transparent hover:border-gray-200 ${ - activeIndex === index ? "border-gray-700" : "" - }`} - /> - - )} - - ); -} diff --git a/app/components/Grids/Common/HeadCell/index.ts b/app/components/Grids/Common/HeadCell/index.ts deleted file mode 100644 index c5f8b33..0000000 --- a/app/components/Grids/Common/HeadCell/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -import HeadCell from "./HeadCell"; - -export default HeadCell; diff --git a/app/components/Grids/Common/Table/Table.tsx b/app/components/Grids/Common/Table/Table.tsx deleted file mode 100644 index 8df19d5..0000000 --- a/app/components/Grids/Common/Table/Table.tsx +++ /dev/null @@ -1,94 +0,0 @@ -import Spinner from "~/components/Icons/Spinner"; -import type { GridBottle } from "~/models/bottle.server"; -import type { Column } from "../../TestGrid/NewTestGrid"; - -type TableProps = { - columns: Column[]; - data: GridBottle[] | []; - loading: boolean; -}; - -export default function Table({ columns, data, loading }: TableProps) { - return ( -
-
- - - - {columns.map((column, index) => ( - - ))} - - - - <> - {data.map((item, index) => ( - - {loading ? ( - - ) : ( - <> - - - - - - - - - - - - - - - - )} - - ))} - - -
- {column.header} -
- {item.name} - - {item.status} - - {item.type} - - {item.distiller} - - {item.producer} - - {item.price} - - {item.batch} - - {item.alcoholPercent} - - {item.proof} - - {item.country} - - {item.region} - - {item.color} - - {item.finishing} - - {item.size} -
-
-
- ); -} diff --git a/app/components/Grids/Common/Table/index.ts b/app/components/Grids/Common/Table/index.ts deleted file mode 100644 index ec5c448..0000000 --- a/app/components/Grids/Common/Table/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -import Table from "./Table"; - -export default Table; diff --git a/app/components/Grids/Common/TableBody/TableBody.tsx b/app/components/Grids/Common/TableBody/TableBody.tsx deleted file mode 100644 index 6ec0bb2..0000000 --- a/app/components/Grids/Common/TableBody/TableBody.tsx +++ /dev/null @@ -1,132 +0,0 @@ -import type { GridBottle } from "~/models/bottle.server"; - -type TableBodyProps = { - items: GridBottle[] | []; -}; - -export default function TableBody({ items }: TableBodyProps) { - return ( - - {items.map((bottle, index) => ( - - - {bottle.name} - - - - {bottle.status} - - - - {bottle.type} - - - {bottle.distiller} - - - {bottle.producer} - - - ${bottle.price} - - - {bottle.batch} - - - {bottle.alcoholPercent}% - - - {bottle.proof}pf - - - {bottle.country} - - - {bottle.region} - - - {bottle.color} - - - {bottle.finishing} - - - {bottle.size} - - - ))} - - ); -} diff --git a/app/components/Grids/Common/Thead/Thead.tsx b/app/components/Grids/Common/Thead/Thead.tsx deleted file mode 100644 index 4419b6f..0000000 --- a/app/components/Grids/Common/Thead/Thead.tsx +++ /dev/null @@ -1,47 +0,0 @@ -import type { Column } from "~/routes/services/search/bottle/fetch"; -import type { SortDirection, SortFields } from "../../TestGrid/TestGrid"; -import HeadCell from "../HeadCell"; - -type TheadProps = { - columns: Column[]; - sortField: SortFields; - sortDirection: SortDirection; - tableHeight: string | number; - activeIndex: number | null; - mouseDown: (index: number) => void; - handleSortingChange: (sortField: SortFields) => void; -}; - -export default function Thead({ - columns, - sortField, - sortDirection, - tableHeight, - activeIndex, - mouseDown, - handleSortingChange, -}: TheadProps) { - return ( - - - {columns.map((column, index) => ( - - ))} - - - ); -} diff --git a/app/components/Grids/Common/Thead/index.ts b/app/components/Grids/Common/Thead/index.ts deleted file mode 100644 index 33f7172..0000000 --- a/app/components/Grids/Common/Thead/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -import Thead from "./Thead"; - -export default Thead; diff --git a/app/components/Grids/ReviewTable/ReviewTable.tsx b/app/components/Grids/ReviewTable/ReviewTable.tsx deleted file mode 100644 index c4a0bcc..0000000 --- a/app/components/Grids/ReviewTable/ReviewTable.tsx +++ /dev/null @@ -1,233 +0,0 @@ -import type { Prisma } from "@prisma/client"; -import { Link } from "@remix-run/react"; -import ExternalLink from "~/components/Icons/ExternalLink"; -import FilterArrowDown from "~/components/Icons/FilterArrowDown"; -import FilterArrows from "~/components/Icons/FilterArrows"; -import FilterArrowUp from "~/components/Icons/FilterArrowUp"; -import type { - SortableColumn, - UnsortableColumn, - ReviewSortOptions, - GridReview, -} from "~/routes/services/search/review/fetch"; -import Caption from "../Common/Caption"; - -type Column = SortableColumn | UnsortableColumn; - -export type ReviewTableProps = { - columns: Column[]; - items: GridReview[] | []; - sortField: ReviewSortOptions; - sortDirection: Prisma.SortOrder; - handleSortingChange: (field: ReviewSortOptions) => void; -}; - -export default function ReviewTable({ - columns, - items, - sortField, - sortDirection, - handleSortingChange, -}: ReviewTableProps) { - return ( -
-
- - - - {columns.map((column, index) => ( - <> - {column.kind === "sortable" && column.sort ? ( - - ) : ( - - )} - - ))} - - - - {items.map((review, index) => ( - - - - - - - - - - - - - - - - ))} - -
-
handleSortingChange(column.field)} - className="border-b-2 text-left text-gray-700 first-of-type:sticky first-of-type:left-0 first-of-type:w-[300px] first-of-type:min-w-[300px] first-of-type:bg-white" - > -
-
{column.header}
-
- {column.sort && - column.field === sortField && - sortDirection === "asc" ? ( - - ) : column.field === sortField && - sortDirection === "desc" ? ( - - ) : ( - - )} -
-
-
- {column.header} -
- {review?.bottle?.name} - - {new Date(review?.date as string).toLocaleString("en-us", { - year: "2-digit", - month: "2-digit", - day: "2-digit", - })} - - - {review?.bottle?.status} - - - {review?.bottle?.type} - - {review?.bottle?.distiller} - - {review?.bottle?.producer} - - {review?.bottle?.alcoholPercent}% - - {review?.bottle?.proof}pf - - {review?.bottle?.age} - - ${review?.bottle?.price} - - {review?.value} - - {review?.overallRating} - - - - -
-
-
- ); -} diff --git a/app/components/Grids/ReviewTable/index.ts b/app/components/Grids/ReviewTable/index.ts deleted file mode 100644 index a63a516..0000000 --- a/app/components/Grids/ReviewTable/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -import ReviewTable from "./ReviewTable"; - -export default ReviewTable; diff --git a/app/components/Grids/TestGrid/NewTestGrid.tsx b/app/components/Grids/TestGrid/NewTestGrid.tsx deleted file mode 100644 index 82c9754..0000000 --- a/app/components/Grids/TestGrid/NewTestGrid.tsx +++ /dev/null @@ -1,183 +0,0 @@ -import { useCallback, useEffect, useMemo, useState } from "react"; -import { useTypedFetcher } from "remix-typedjson"; -import Spinner from "~/components/Icons/Spinner"; -import type { GridBottle } from "~/models/bottle.server"; -import type { BottleSearchData } from "~/routes/services/search/bottle"; -import useDebounce from "~/utils/useDebounce"; -import GlobalFilter from "../Common/GlobalFilter"; -import Pagination from "../Common/Pagination/Pagination"; -import Table from "../Common/Table"; - -export type Limit = 10 | 25 | 50 | 75 | 100 | 250; - -export type BottleData = { - bottles: GridBottle[] | []; - totalPages: number; -}; - -export type Column = { - header: string; - field: keyof GridBottle; -}; - -export default function NewTestGrid() { - const [data, setData] = useState({ - bottles: [], - totalPages: 0, - }); - const [currentPage, setCurrentPage] = useState(0); - const [limit, setLimit] = useState(10); - const [query, setQuery] = useState(""); - const [loading, setLoading] = useState(false); - const searchFetcher = useTypedFetcher(); - const searchTerm = useDebounce(query, 300); - - const columns: Column[] = useMemo( - () => [ - { - header: "Name", - field: "name", - }, - { - header: "Status", - field: "status", - }, - { - header: "Type", - field: "type", - }, - { - header: "Distiller", - field: "distiller", - }, - { - header: "Producer", - field: "producer", - }, - { - header: "Price", - field: "price", - }, - { - header: "Barrel #", - field: "batch", - }, - { - header: "ABV", - field: "alcoholPercent", - }, - { - header: "Proof", - field: "proof", - }, - { - header: "Country", - field: "country", - }, - { - header: "Region", - field: "region", - }, - { - header: "Color", - field: "color", - }, - { - header: "Finishing", - field: "finishing", - }, - { - header: "Size", - field: "size", - }, - ], - [] - ); - - const { load, state, type, data: fetcherData } = searchFetcher; - - useEffect(() => { - if (!fetcherData || state === "loading") { - return; - } - if (fetcherData) { - const newItems = fetcherData.data; - const totalPages = fetcherData.totalPages; - setData({ - bottles: newItems, - totalPages, - }); - } - }, [fetcherData, state]); - - const onFilterChange = useCallback(() => { - setLoading(true); - load(`/services/search/bottle?query=${searchTerm}&page=0&limit=${limit}`); - if (type === "done") { - setData({ - bottles: fetcherData.data, - totalPages: fetcherData.totalPages, - }); - } - setLoading(false); - }, [limit, load, searchTerm, fetcherData, type]); - - const onPaginationChange = useCallback( - (page: number) => { - setLoading(true); - load(`/services/search/bottle?query=&page=${page}&limit=${limit}`); - if (type == "done") { - setData({ - bottles: fetcherData?.data, - totalPages: fetcherData?.totalPages, - }); - } - setLoading(false); - }, - [limit, load, fetcherData, type] - ); - - const handleQueryChange = useCallback((newQuery: string) => { - setQuery(newQuery); - }, []); - - const handlePageChange = useCallback((newPage: number) => { - setCurrentPage(newPage); - }, []); - - const firstPage = () => { - setCurrentPage(0); - onPaginationChange(0); - }; - - const fetchData = useCallback(() => { - setLoading(true); - if (type === "init") { - load(`/services/search/bottle?query=&page=${currentPage}&limit=${limit}`); - } - if (type === "done") { - setData({ - bottles: fetcherData.data, - totalPages: fetcherData.totalPages, - }); - } - setLoading(false); - }, [type, load, fetcherData, limit, currentPage]); - - useEffect(() => { - fetchData(); - }, [fetchData]); - - return ( -
- {/* handleQueryChange(e.target.value)} - // load={load} - searchTerm={searchTerm} - limit={limit} - onFilterChange={onFilterChange} - /> */} - - - ); -} diff --git a/app/components/Grids/TestGrid/TestGrid.tsx b/app/components/Grids/TestGrid/TestGrid.tsx deleted file mode 100644 index 9695db3..0000000 --- a/app/components/Grids/TestGrid/TestGrid.tsx +++ /dev/null @@ -1,212 +0,0 @@ -import { useCallback, useEffect, useMemo, useState, useRef } from "react"; -import type { ChangeEvent } from "react"; -import { useTypedFetcher } from "remix-typedjson"; -import type { - BottleSearchData, - Column, - Limit, -} from "~/routes/services/search/bottle/fetch"; -import type {} from "./NewTestGrid"; -import useDebounce from "~/utils/useDebounce"; -import GlobalFilter from "../Common/GlobalFilter"; -import Pagination from "../Common/Pagination/Pagination"; -import BottleTable from "../BottleTable/BottleTable"; - -export type SortDirection = "asc" | "desc"; -export type SortFields = - | "name" - | "status" - | "type" - | "distiller" - | "producer" - | "country" - | "region"; - -export default function Grid() { - const [currentPage, setCurrentPage] = useState(0); - const [limit, setLimit] = useState(10); - const [query, setQuery] = useState(""); - const [sortField, setSortField] = useState("name"); - const [sortDirection, setSortDirection] = useState("asc"); - const [tableHeight, setTableHeight] = useState("auto"); - const [activeIndex, setActiveIndex] = useState(null); - const tableRef = useRef(null); - const searchTerm = useDebounce(query, 300); - - const columns: Column[] = [ - { - header: "Name", - field: "name", - sort: true, - sortDirection: "desc", - ref: useRef(null), - }, - { - header: "Status", - field: "status", - sort: true, - sortDirection: "desc", - ref: useRef(null), - }, - { - header: "Type", - field: "type", - sort: true, - sortDirection: "desc", - ref: useRef(null), - }, - { - header: "Distiller", - field: "distiller", - sort: true, - sortDirection: "desc", - ref: useRef(null), - }, - { - header: "Producer", - field: "producer", - sort: true, - sortDirection: "desc", - ref: useRef(null), - }, - { - header: "Price", - field: "price", - sort: false, - sortDirection: "desc", - ref: useRef(null), - }, - { - header: "Barrel #", - field: "batch", - sort: false, - sortDirection: "desc", - ref: useRef(null), - }, - { - header: "ABV", - field: "alcoholPercent", - sort: false, - sortDirection: "desc", - ref: useRef(null), - }, - { - header: "Proof", - field: "proof", - sort: false, - sortDirection: "desc", - ref: useRef(null), - }, - { - header: "Country", - field: "country", - sort: true, - sortDirection: "desc", - ref: useRef(null), - }, - { - header: "Region", - field: "region", - sort: true, - sortDirection: "desc", - ref: useRef(null), - }, - { - header: "Color", - field: "color", - sort: false, - sortDirection: "desc", - ref: useRef(null), - }, - { - header: "Finishing", - field: "finishing", - sort: false, - sortDirection: "desc", - ref: useRef(null), - }, - { - header: "Size", - field: "size", - sort: false, - sortDirection: "desc", - ref: useRef(null), - }, - ]; - - const { data, load } = useTypedFetcher(); - const items = useMemo(() => { - return data?.items || []; - }, [data]); - - const totalPages = data?.totalPages || 0; - const totalItems = data?.totalBottles || 0; - - const getInitialData = useCallback(() => { - load(`/services/search/bottle/fetch?page=0&limit=${limit}`); - }, [load, limit]); - - useEffect(() => { - getInitialData(); - }, [getInitialData]); - - useEffect(() => { - function loadPageData() { - load( - `/services/search/bottle/fetch?query=${searchTerm}&page=${currentPage}&limit=${limit}&sort=${sortField}&direction=${sortDirection}` - ); - } - loadPageData(); - }, [load, currentPage, limit, searchTerm, sortField, sortDirection]); - - useEffect(() => { - const offsetHeight = tableRef.current?.offsetHeight; - if (offsetHeight) { - setTableHeight(offsetHeight); - } - }, []); - - const onFirst = () => setCurrentPage(0); - const onLast = () => setCurrentPage(totalPages - 1); - const handleQueryChange = (e: ChangeEvent) => - setQuery(e.target.value); - - const handleSortingChange = (field: SortFields) => { - const sortOrder = - field === sortField && sortDirection === "asc" ? "desc" : "asc"; - setSortField(field); - setSortDirection(sortOrder); - }; - const mouseDown = (index: number) => setActiveIndex(index); - - return ( -
-
- - - -
-
- ); -} diff --git a/app/components/Icons/ArrowIcon.tsx b/app/components/Icons/ArrowIcon.tsx deleted file mode 100644 index 3b13499..0000000 --- a/app/components/Icons/ArrowIcon.tsx +++ /dev/null @@ -1,16 +0,0 @@ -export default function ArrowIcon() { - return ( - - - - ); -} diff --git a/app/components/Icons/ChevronDoubleLeft.tsx b/app/components/Icons/ChevronDoubleLeft.tsx deleted file mode 100644 index b2f8b6f..0000000 --- a/app/components/Icons/ChevronDoubleLeft.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import { classNames } from "~/utils/cssHelper"; - -interface IChevronProps { - className?: string; -} - -export default function ChevronDoubleLeft({ className }: IChevronProps) { - return ( - - - - ); -} diff --git a/app/components/Icons/ChevronDoubleRight.tsx b/app/components/Icons/ChevronDoubleRight.tsx deleted file mode 100644 index 2cfde74..0000000 --- a/app/components/Icons/ChevronDoubleRight.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import { classNames } from "~/utils/cssHelper"; - -interface IChevronProps { - className?: string; -} - -export default function ChevronDoubleRight({ className }: IChevronProps) { - return ( - - - - - ); -} diff --git a/app/components/Icons/ChevronLeft.tsx b/app/components/Icons/ChevronLeft.tsx deleted file mode 100644 index 6070320..0000000 --- a/app/components/Icons/ChevronLeft.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import { classNames } from "~/utils/cssHelper"; - -interface IChevronProps { - className?: string; -} - -export default function ChevronLeft({ className }: IChevronProps) { - return ( - - - - ); -} diff --git a/app/components/Icons/DeleteIcon.tsx b/app/components/Icons/DeleteIcon.tsx deleted file mode 100644 index 6c89741..0000000 --- a/app/components/Icons/DeleteIcon.tsx +++ /dev/null @@ -1,18 +0,0 @@ -export default function DeleteIcon() { - return ( - - - - ); -} diff --git a/app/components/Icons/Ellipses.tsx b/app/components/Icons/Ellipses.tsx deleted file mode 100644 index 0fe2f14..0000000 --- a/app/components/Icons/Ellipses.tsx +++ /dev/null @@ -1,22 +0,0 @@ -type EllipsesProps = { - classes?: string; -}; - -export default function Ellipses({ classes }: EllipsesProps) { - return ( - - - - ); -} diff --git a/app/components/Icons/ErrorIcon.tsx b/app/components/Icons/ErrorIcon.tsx deleted file mode 100644 index ad31d43..0000000 --- a/app/components/Icons/ErrorIcon.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import { classNames } from "~/utils/cssHelper"; - -interface IErrorIconProps { - className?: string; -} - -export default function ErrorIcon({ className }: IErrorIconProps) { - return ( - - - - ); -} diff --git a/app/components/Icons/FilterArrowUp.tsx b/app/components/Icons/FilterArrowUp.tsx deleted file mode 100644 index 1a65b3e..0000000 --- a/app/components/Icons/FilterArrowUp.tsx +++ /dev/null @@ -1,18 +0,0 @@ -export default function FilterArrowUp() { - return ( - - - - ); -} diff --git a/app/components/Icons/FilterArrows.tsx b/app/components/Icons/FilterArrows.tsx deleted file mode 100644 index a48a3c8..0000000 --- a/app/components/Icons/FilterArrows.tsx +++ /dev/null @@ -1,18 +0,0 @@ -export default function FilterArrows() { - return ( - - - - ); -} diff --git a/app/components/Icons/FilterIcon.tsx b/app/components/Icons/FilterIcon.tsx deleted file mode 100644 index c934b84..0000000 --- a/app/components/Icons/FilterIcon.tsx +++ /dev/null @@ -1,18 +0,0 @@ -export default function FilterIcon() { - return ( - - - - ); -} diff --git a/app/components/Icons/FollowIcon 2.tsx b/app/components/Icons/FollowIcon 2.tsx deleted file mode 100644 index f71e58a..0000000 --- a/app/components/Icons/FollowIcon 2.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import { classNames } from "~/utils/cssHelper"; - -const FollowIcon = ({ classes }: { classes: string }) => { - return ( - - - - ); -}; - -export default FollowIcon; diff --git a/app/components/Icons/Plus 2.tsx b/app/components/Icons/Plus 2.tsx deleted file mode 100644 index 82926e3..0000000 --- a/app/components/Icons/Plus 2.tsx +++ /dev/null @@ -1,22 +0,0 @@ -type PlusProps = { - classes?: string; -}; - -export default function Plus({ classes }: PlusProps) { - return ( - - - - ); -} diff --git a/app/components/Icons/Plus.tsx b/app/components/Icons/Plus.tsx deleted file mode 100644 index 82926e3..0000000 --- a/app/components/Icons/Plus.tsx +++ /dev/null @@ -1,22 +0,0 @@ -type PlusProps = { - classes?: string; -}; - -export default function Plus({ classes }: PlusProps) { - return ( - - - - ); -} diff --git a/app/components/Icons/FilterArrowDown.tsx b/app/components/Icons/RightArrowCircle.tsx similarity index 70% rename from app/components/Icons/FilterArrowDown.tsx rename to app/components/Icons/RightArrowCircle.tsx index 8584040..6ec0403 100644 --- a/app/components/Icons/FilterArrowDown.tsx +++ b/app/components/Icons/RightArrowCircle.tsx @@ -1,4 +1,4 @@ -export default function FilterArrowDown() { +export default function RightArrowCircle() { return ( ); diff --git a/app/components/Icons/Sort.tsx b/app/components/Icons/Sort.tsx index 0a8a532..5f30fb4 100644 --- a/app/components/Icons/Sort.tsx +++ b/app/components/Icons/Sort.tsx @@ -1,16 +1,17 @@ export default function Sort({ className }: { className: string }) { return ( - + + + ); } diff --git a/app/components/Icons/SortDown.tsx b/app/components/Icons/SortDown.tsx index bf242a7..ebd12ad 100644 --- a/app/components/Icons/SortDown.tsx +++ b/app/components/Icons/SortDown.tsx @@ -1,16 +1,16 @@ export default function SortDown({ className }: { className: string }) { return ( - + + ); } diff --git a/app/components/Icons/SortUp.tsx b/app/components/Icons/SortUp.tsx index 711fe0e..e768e70 100644 --- a/app/components/Icons/SortUp.tsx +++ b/app/components/Icons/SortUp.tsx @@ -1,16 +1,16 @@ export default function SortUp({ className }: { className: string }) { return ( - + + ); } diff --git a/app/components/Icons/Swatch.tsx b/app/components/Icons/Swatch.tsx deleted file mode 100644 index cd7cab8..0000000 --- a/app/components/Icons/Swatch.tsx +++ /dev/null @@ -1,18 +0,0 @@ -export default function Swatch() { - return ( - - - - ); -} diff --git a/app/components/Icons/User 2.tsx b/app/components/Icons/User 2.tsx deleted file mode 100644 index 77de3c1..0000000 --- a/app/components/Icons/User 2.tsx +++ /dev/null @@ -1,22 +0,0 @@ -type UserProps = { - className?: string; -}; - -export default function User({ className }: UserProps) { - return ( - - - - ); -} diff --git a/app/components/Icons/UserGroup.tsx b/app/components/Icons/UserGroup.tsx deleted file mode 100644 index ab8a236..0000000 --- a/app/components/Icons/UserGroup.tsx +++ /dev/null @@ -1,18 +0,0 @@ -export default function UserGroup() { - return ( - - - - ); -} diff --git a/app/components/Other/BottleGrid/BottleGrid.tsx b/app/components/Other/BottleGrid/BottleGrid.tsx deleted file mode 100644 index 2129c72..0000000 --- a/app/components/Other/BottleGrid/BottleGrid.tsx +++ /dev/null @@ -1,160 +0,0 @@ -import type { ChangeEvent } from "react"; -import { useCallback, useState, useEffect } from "react"; -import { useSort } from "@table-library/react-table-library/sort"; -import { useCustom } from "@table-library/react-table-library"; -import { useTypedFetcher } from "remix-typedjson"; -import type { BottleSearchData } from "~/routes/services/search/bottle"; -import type { GridBottle } from "~/models/bottle.server"; -import { usePagination } from "@table-library/react-table-library/pagination"; -import type { Action, State } from "@table-library/react-table-library/types"; -import { useSearchParams } from "@remix-run/react"; -import useDebounce from "~/utils/useDebounce"; -import FilterArrowUp from "~/components/Icons/FilterArrowUp"; -import FilterArrowDown from "~/components/Icons/FilterArrowDown"; -import FilterArrows from "~/components/Icons/FilterArrows"; -// import GridHeader from "../BottleGrid/GridHeader/GridHeader"; -// import GridBody from "../BottleGrid/GridBody/GridBody"; -// import GridFooter from "../BottleGrid/GridFooter/GridFooter"; - -type Limit = 10 | 25 | 50 | 100 | 250; - -type Data = { - nodes: GridBottle[] | []; - totalPages: number; -}; - -export default function TestGrid() { - const [data, setData] = useState({ - nodes: [], - totalPages: 0, - }); - const [query, setQuery] = useState(""); - const [limit, setLimit] = useState(10); - const [isLoading, setIsLoading] = useState(false); - const [page, setPage] = useState(0); - const searchFetcher = useTypedFetcher(); - const pagination = usePagination( - data, - { - state: { - page, - size: limit, - }, - onChange: onPaginationChange, - }, - { isServer: true } - ); - const sort = useSort( - data, - { - state: { - sortKey: "NAME", - reverse: false, - }, - onChange: onSortChange, - }, - { - sortIcon: { - margin: "0px", - iconDefault: , - iconUp: , - iconDown: , - }, - sortFns: { - NAME: (array) => array.sort((a, b) => a.name.localeCompare(b.name)), - STATUS: (array) => - array.sort((a, b) => a.status.localeCompare(b.status)), - TYPE: (array) => array.sort((a, b) => a.type.localeCompare(b.type)), - DISTILLER: (array) => - array.sort((a, b) => a.distiller.localeCompare(b.distiller)), - PRODUCER: (array) => - array.sort((a, b) => a.producer.localeCompare(b.producer)), - ABV: (array) => - array.sort((a, b) => a.alcoholPercent - b.alcoholPercent), - PROOF: (array) => array.sort((a, b) => a.proof - b.proof), - PRICE: (array) => array.sort((a, b) => a.price - b.price), - COUNTRY: (array) => - array.sort((a, b) => a.country.localeCompare(b.country)), - REGION: (array) => - array.sort((a, b) => a.region.localeCompare(b.region)), - }, - } - ); - - function onSortChange(action: Action, state: State) { - console.log(`STATE: ${JSON.stringify(state)}`); - } - - const [params] = useSearchParams(); - const searchTerm = useDebounce(query, 300); - - useCustom("search", data, { - state: { searchTerm }, - onChange: onSearchChange, - }); - - function onSearchChange(action: Action, state: State) { - searchFetcher.load( - `/services/search/bottle?query=${state.searchTerm}&page=0&limit=${limit}` - ); - } - - const handleSearch = (e: ChangeEvent) => { - setQuery(e.target.value); - }; - - function onPaginationChange(action: Action, state: State) { - setPage(state.page); - searchFetcher.load( - `/services/search/bottle?query=${ - state.searchTerm || "" - }&page=${page}&limit=${limit}` - ); - if (searchFetcher.type === "done") { - setData({ - nodes: searchFetcher.data.data, - totalPages: searchFetcher.data.totalPages, - }); - } - } - - const fetchData = useCallback(() => { - setIsLoading(true); - if (searchFetcher.type === "init") { - searchFetcher.load( - `/services/search/bottle?query=&page=0&limit=${limit}` - ); - } - if (searchFetcher.type === "done") { - setData({ - nodes: searchFetcher.data.data, - totalPages: searchFetcher.data.totalPages, - }); - } - setIsLoading(false); - }, [searchFetcher, limit]); - - useEffect(() => { - fetchData(); - }, [fetchData]); - - return ( - <> - {/* - - */} - - ); -} diff --git a/app/components/Other/BottleGrid/index.ts b/app/components/Other/BottleGrid/index.ts deleted file mode 100644 index 2e00747..0000000 --- a/app/components/Other/BottleGrid/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -import BottleGrid from "./BottleGrid"; - -export default BottleGrid; diff --git a/app/components/Other/GlobalFilter/GlobalFilter.tsx b/app/components/Other/GlobalFilter/GlobalFilter.tsx deleted file mode 100644 index 4cb774a..0000000 --- a/app/components/Other/GlobalFilter/GlobalFilter.tsx +++ /dev/null @@ -1 +0,0 @@ -export default function GlobalFilter() {} diff --git a/app/components/Other/GlobalFilter/index.ts b/app/components/Other/GlobalFilter/index.ts deleted file mode 100644 index 9d49166..0000000 --- a/app/components/Other/GlobalFilter/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -import GlobalFilter from "./GlobalFilter"; - -export default GlobalFilter; diff --git a/app/components/Other/ReviewGrid/ReviewGrid.tsx b/app/components/Other/ReviewGrid/ReviewGrid.tsx deleted file mode 100644 index 57211d6..0000000 --- a/app/components/Other/ReviewGrid/ReviewGrid.tsx +++ /dev/null @@ -1,484 +0,0 @@ -import { useTheme } from "@table-library/react-table-library/theme"; -import { - Table, - Header, - HeaderRow, - HeaderCell, - Body, - Row, - Cell, - useCustom, -} from "@table-library/react-table-library"; -import { usePagination } from "@table-library/react-table-library/pagination"; -import { useCallback, useEffect, useState } from "react"; -import type { ChangeEvent } from "react"; -import { useTypedFetcher } from "remix-typedjson"; -import type { ReviewSearchData } from "~/routes/services/search/review"; -import { Link, useSearchParams } from "@remix-run/react"; -import type { - Action, - State, -} from "@table-library/react-table-library/types/common"; -import useDebounce from "~/utils/useDebounce"; -import { Switch, Transition } from "@headlessui/react"; -import ExternalLink from "~/components/Icons/ExternalLink"; -import Spinner from "~/components/Icons/Spinner"; - -export type GridReview = { - id: string; - date: string | null; - overallRating: number | null; - value: number | null; - bottle: { - name: string; - type: string; - distiller: string | null; - producer: string | null; - proof: string | null; - alcoholPercent: string | null; - age: string | null; - price: string | null; - imageUrl: string | null; - } | null; -}; - -type Data = { - nodes: GridReview[] | []; - totalPages?: number; -}; - -export default function ReviewGrid() { - const [data, setData] = useState({ - nodes: [], - totalPages: 0, - }); - const [query, setQuery] = useState(""); - const [limit, setLimit] = useState(10); - const [loading, setLoading] = useState(false); - const [showImage, setShowImage] = useState(false); - const [page, setPage] = useState(0); - const searchFetcher = useTypedFetcher(); - const theme = useTheme({}); - const pagination = usePagination( - data, - { - state: { - page: page, - size: limit, - }, - onChange: onPaginationChange, - }, - { isServer: true } - ); - const [params] = useSearchParams(); - const searchTerm = useDebounce(query, 300); - - useCustom("search", data, { - state: { searchTerm }, - onChange: onSearchChange, - }); - - function onSearchChange(action: Action, state: State) { - searchFetcher.load( - `/services/search/review?query=${state.searchTerm}&page=0&limit=${limit}` - ); - } - - const handleSearch = (e: ChangeEvent) => { - setQuery(e.target.value); - }; - - function onPaginationChange(action: Action, state: State) { - setPage(state.page); - searchFetcher.load( - `/services/search/review?query=${ - state.searchTerm || "" - }&page=${page}&limit=${limit}` - ); - if (searchFetcher.type === "done") { - setData({ - nodes: searchFetcher.data.data, - totalPages: searchFetcher.data.totalPages, - }); - } - } - - const fetchData = useCallback(() => { - setLoading(true); - if (searchFetcher.type === "init") { - searchFetcher.load( - `/services/search/review?query=&page=0&limit=${limit}` - ); - } - if (searchFetcher.type === "done") { - setData({ - nodes: searchFetcher.data.data, - totalPages: searchFetcher.data.totalPages, - }); - } - setLoading(false); - }, [searchFetcher, limit]); - - useEffect(() => { - fetchData(); - }, [fetchData]); - - return ( -
-
- {/* TABLE SEARCH / TOGGLE */} -
-
- -
-
-
- {showImage ? "Toggle to hide images" : "Toggle to show images"} -
-
- - - {showImage - ? "Toggle to hide images" - : "Toggle to show images"} - - - -
-
-
- {/* TABLE */} - {loading ? ( -
- -
- ) : ( -
- {" "} -
- {(tableList) => ( - <> -
- - - - Bottle - - - - Name - - - Date - - - Type - - - Distillery - - - Producer - - - ABV - - - Proof - - - Age - - - Price - - - Rating - - - Value - - - Link - - -
- - - {tableList.map((item) => { - return ( - - - - {`Bottle - - - - {item.bottle.name} - - - {new Date(item.date).toLocaleString("en-US", { - year: "numeric", - month: "2-digit", - day: "2-digit", - })} - - - {item.bottle.type} - - - {item.bottle.distiller} - - - {item.bottle.producer} - - - {item.bottle.alcoholPercent} - - - {item.bottle.proof} - - - {item.bottle.age} - - - ${item.bottle.price} - - -
= 7.5 - ? `bg-green-100 text-green-700` - : item.overallRating >= 5 && - item.overallRating < 7.5 - ? `bg-yellow-100 text-yellow-700` - : item.overallRating < 5 - ? `bg-red-100 text-red-100` - : "" + - ` leading-wide mx-4 rounded-lg p-4 px-3 py-1 text-center text-xs font-bold uppercase shadow-sm` - } - > - {item.overallRating} -
-
- -
= 7.5 - ? `bg-green-100 text-green-700` - : item.overallRating >= 5 && - item.overallRating < 7.5 - ? `bg-yellow-100 text-yellow-700` - : item.overallRating < 5 - ? `bg-red-100 text-red-100` - : "" + - ` leading-wide mx-4 rounded-lg p-4 px-3 py-1 text-center text-xs font-bold uppercase shadow-sm` - } - > - {item.value} -
-
- - - - - -
- ); - })} - - - )} -
-
- )} - {/* PAGINATION */} -
-
- Total Pages: {searchFetcher?.data?.totalPages} -
- - - - {Array(data.totalPages) - .fill(data.totalPages) - .map((_: any, index: any) => ( - - ))} - - -
- Page: {page + 1} of {searchFetcher?.data?.totalPages} -
-
-
-
- ); -} diff --git a/app/components/Other/ReviewGrid/index.ts b/app/components/Other/ReviewGrid/index.ts deleted file mode 100644 index 847a961..0000000 --- a/app/components/Other/ReviewGrid/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -import ReviewGrid from "./ReviewGrid"; - -export default ReviewGrid; diff --git a/app/components/Review/Bottle/Bottle.tsx b/app/components/Review/Bottle/Bottle.tsx index 0638b1c..3c55f0f 100644 --- a/app/components/Review/Bottle/Bottle.tsx +++ b/app/components/Review/Bottle/Bottle.tsx @@ -47,7 +47,7 @@ export default function Bottle({ bottle, reviewId }: BottleProps) { />
)} -
+
} /> diff --git a/app/components/Review/Grid/ABVRenderer/ABVRenderer.tsx b/app/components/Review/Grid/ABVRenderer/ABVRenderer.tsx deleted file mode 100644 index 1aae7c3..0000000 --- a/app/components/Review/Grid/ABVRenderer/ABVRenderer.tsx +++ /dev/null @@ -1,7 +0,0 @@ -export default function ABVRenderer(params: any) { - return ( -
- {params.value}% -
- ); -} diff --git a/app/components/Review/Grid/ABVRenderer/index.tsx b/app/components/Review/Grid/ABVRenderer/index.tsx deleted file mode 100644 index 764d17b..0000000 --- a/app/components/Review/Grid/ABVRenderer/index.tsx +++ /dev/null @@ -1,3 +0,0 @@ -import ABVRenderer from "./ABVRenderer"; - -export default ABVRenderer; diff --git a/app/components/Review/Grid/DataGrid/DataGrid.tsx b/app/components/Review/Grid/DataGrid/DataGrid.tsx deleted file mode 100644 index 5e6fd08..0000000 --- a/app/components/Review/Grid/DataGrid/DataGrid.tsx +++ /dev/null @@ -1,439 +0,0 @@ -import { useState, useRef, useMemo, useCallback, useEffect } from "react"; -import ImageRenderer from "../ImageRenderer/ImageRenderer"; -import RatingRenderer from "../RatingRenderer/RatingRenderer"; -import LinkRenderer from "../LinkRenderer/LinkRenderer"; -import PriceRenderer from "../PriceRenderer/PriceRenderer"; -import ABVRenderer from "~/components/Review/Grid/ABVRenderer/ABVRenderer"; -import ProofRenderer from "../ProofRenderer/ProofRenderer"; -import AddIcon from "~/components/Icons/AddIcon"; -import { Link } from "@remix-run/react"; - -type NumberParserParams = { - newValue: string; -}; - -const numberParser = (params: NumberParserParams) => { - const newValue = params.newValue; - let valueAsNumber; - if (newValue === null || newValue === undefined || newValue === "") { - valueAsNumber = null; - } else { - valueAsNumber = parseFloat(params.newValue); - } - return valueAsNumber; -}; - -// function nameGetter(params: ValueGetterParams) { -// if (!params.data) return `loading...`; -// return { -// name: params.data.name, -// imageUrl: params.data.imageUrl, -// }; -// } - -export default function DataGrid({ initialData }: any) { - // const grid = useRef(null); - const gridStyle = useMemo(() => ({ height: "100%", width: "100%" }), []); - const [records, setRecords] = useState(0); - const [rowData, setRowData] = useState(initialData); - // const [columnDefs, setColumnDefs] = useState([ - // { - // field: "name", - // minWidth: 300, - // maxWidth: 350, - // lockPosition: "left", - // valueGetter: nameGetter, - // cellRenderer: ImageRenderer, - // cellStyle: { display: "flex" }, - // filter: "agTextColumnFilter", - // filterParams: { - // buttons: ["apply", "reset"], - // closeOnApply: true, - // }, - // getQuickFilterText: (params) => params.value, - // icons: { - // menu: ` - //
- // - // - // - //
- // `, - // }, - // tooltipComponent: NameTooltip, - // tooltipField: "name", - // }, - // { - // field: "date", - // sort: "desc", - // minWidth: 130, - // maxWidth: 130, - // cellStyle: { - // display: "flex", - // justifyContent: "center", - // alignItems: "center", - // fontWeight: 500, - // }, - // filter: "agDateColumnFilter", - // filterParams: { - // buttons: ["apply", "reset"], - // closeOnApply: true, - // }, - // icons: { - // menu: ` - //
- // - // - // - //
- // `, - // }, - // }, - // { - // field: "type", - // minWidth: 150, - // maxWidth: 150, - // cellStyle: { - // display: "flex", - // // justifyContent: "center", - // alignItems: "center", - // fontWeight: "500", - // }, - // filter: "agTextColumnFilter", - // filterParams: { - // buttons: ["apply", "reset"], - // closeOnApply: true, - // }, - // getQuickFilterText: (params) => params.value, - // icons: { - // menu: ` - //
- // - // - // - //
- // `, - // }, - // }, - // { - // field: "price", - // minWidth: 97, - // maxWidth: 100, - // cellStyle: { - // display: "flex", - // alignItems: "center", - // }, - // filter: "agNumberColumnFilter", - // filterParams: { - // buttons: ["apply", "reset"], - // closeOnApply: true, - // }, - // cellRenderer: PriceRenderer, - // icons: { - // menu: ` - //
- // - // - // - //
- // `, - // }, - // }, - // { - // headerName: "ABV", - // field: "alcoholPercent", - // minWidth: 92, - // maxWidth: 92, - // cellRenderer: ABVRenderer, - // cellStyle: { - // display: "flex", - // alignItems: "center", - // }, - // filter: "agNumberColumnFilter", - // filterParams: { - // buttons: ["apply", "reset"], - // closeOnApply: true, - // }, - // icons: { - // menu: ` - //
- // - // - // - //
- // `, - // }, - // }, - // { - // field: "proof", - // minWidth: 107, - // maxWidth: 110, - // cellStyle: { - // display: "flex", - // alignItems: "center", - // }, - // cellRenderer: ProofRenderer, - // filter: "agNumberColumnFilter", - // filterParams: { - // buttons: ["apply", "reset"], - // closeOnApply: true, - // }, - // icons: { - // menu: ` - //
- // - // - // - //
- // `, - // }, - // }, - // { - // field: "age", - // minWidth: 115, - // maxWidth: 120, - // cellStyle: { - // display: "flex", - // alignItems: "center", - // fontWeight: 500, - // }, - // filter: "agNumberColumnFilter", - // filterParams: { - // buttons: ["apply", "reset"], - // closeOnApply: true, - // }, - // icons: { - // menu: ` - //
- // - // - // - //
- // `, - // }, - // }, - // { - // field: "distillery", - // minWidth: 100, - // maxWidth: 110, - // cellStyle: { - // display: "flex", - // alignItems: "center", - // }, - // filter: "agTextColumnFilter", - // filterParams: { - // buttons: ["apply", "reset"], - // closeOnApply: true, - // }, - // getQuickFilterText: (params) => params.value, - // icons: { - // menu: ` - //
- // - // - // - //
- // `, - // }, - // }, - // { - // field: "producer", - // minWidth: 100, - // maxWidth: 110, - // cellStyle: { - // display: "flex", - // alignItems: "center", - // }, - // filter: "agTextColumnFilter", - // filterParams: { - // buttons: ["apply", "reset"], - // closeOnApply: true, - // }, - // getQuickFilterText: (params) => params.value, - // icons: { - // menu: ` - //
- // - // - // - //
- // `, - // }, - // }, - // { - // field: "value", - // valueParser: numberParser, - // cellRenderer: RatingRenderer, - // minWidth: 100, - // maxWidth: 100, - // cellStyle: { - // display: "flex", - // justifyContent: "center", - // alignItems: "center", - // }, - // filter: "agNumberColumnFilter", - // filterParams: { - // buttons: ["apply", "reset"], - // closeOnApply: true, - // }, - // icons: { - // menu: ` - //
- // - // - // - //
- // `, - // }, - // }, - // { - // field: "rating", - // valueParser: numberParser, - // cellRenderer: RatingRenderer, - // minWidth: 105, - // maxWidth: 105, - // cellStyle: { - // display: "flex", - // justifyContent: "center", - // alignItems: "center", - // }, - // filter: "agNumberColumnFilter", - // filterParams: { - // buttons: ["apply", "reset"], - // closeOnApply: true, - // }, - // icons: { - // menu: ` - //
- // - // - // - //
- // `, - // }, - // }, - // { - // headerName: "Link", - // field: "reviewId", - // minWidth: 80, - // maxWidth: 80, - // cellRenderer: LinkRenderer, - // cellStyle: { - // display: "flex", - // justifyContent: "center", - // alignItems: "center", - // }, - // sortable: false, - // filter: false, - // }, - // ]); - - // const onFilterTextBoxChanged = useCallback(() => { - // grid!.current!.api.setQuickFilter( - // (document!.getElementById("filter-text-box") as HTMLInputElement).value - // ); - // }, []); - - // const defaultColDef = useMemo( - // () => ({ - // resizable: true, - // sortable: true, - // filter: true, - // }), - // [] - // ); - - // const onFirstDataRendered = useCallback((params: FirstDataRenderedEvent) => { - // grid!.current!.api.sizeColumnsToFit(); - // }, []); - - // const onGridSizeChanged = useCallback((params: GridSizeChangedEvent) => { - // let gridWidth = document!.getElementById("grid-wrapper") - // ?.offsetWidth as number; - // let columnsToShow = []; - // let columnsToHide = []; - // let defaultColumnWidth = 350 + 105 + 80; - - // let allColumns = grid.current?.columnApi.getColumns(); - // if (allColumns && allColumns.length > 0) { - // for (let i = 0; i < allColumns.length; i++) { - // let column = allColumns[i]; - // if ( - // column.getColId() === "name" || - // column.getColId() === "rating" || - // column.getColId() === "reviewId" - // ) { - // columnsToShow.push(column); - // } else { - // defaultColumnWidth += column.getMinWidth() || 0; - // if (defaultColumnWidth > gridWidth) { - // columnsToHide.push(column.getColId()); - // } else { - // columnsToShow.push(column.getColId()); - // } - // } - // } - // } - - // grid!.current!.columnApi.setColumnsVisible(columnsToShow, true); - // grid!.current!.columnApi.setColumnsVisible(columnsToHide, false); - // grid!.current!.api.sizeColumnsToFit(); - // }, []); - - // const onGridReady = useCallback((params: GridReadyEvent) => { - // params.api.sizeColumnsToFit(); - // params.api.refreshCells(); - // }, []); - - // useEffect(() => { - // setRecords(initialData.length); - // }, [initialData]); - - return ( -
-
-
-
- - - -
-

My Reviews

-
- {/* 1 ? `records` : `record` - }`} - className="w-[100%] rounded-md border-2 border-blue-300 p-2 outline-blue-900 sm:w-[50%] md:w-[40%] lg:w-[25%]" - /> */} -
- Filter all column data -
-
-
-
- {/* */} -
-
-
- ); -} diff --git a/app/components/Review/Grid/DataGrid/index.ts b/app/components/Review/Grid/DataGrid/index.ts deleted file mode 100644 index 411579a..0000000 --- a/app/components/Review/Grid/DataGrid/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -import DataGrid from "./DataGrid"; - -export default DataGrid; diff --git a/app/components/Review/Grid/ImageRenderer/ImageRenderer.tsx b/app/components/Review/Grid/ImageRenderer/ImageRenderer.tsx deleted file mode 100644 index b46a3ac..0000000 --- a/app/components/Review/Grid/ImageRenderer/ImageRenderer.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import Spinner from "~/components/Icons/Spinner"; - -export default function ImageRenderer(props: any) { - return ( -
- {props.value === undefined ? ( - - ) : ( -
-
- {`Bottle -
-
-

{props.value.name}

-
-
- )} -
- ); -} diff --git a/app/components/Review/Grid/ImageRenderer/index.ts b/app/components/Review/Grid/ImageRenderer/index.ts deleted file mode 100644 index 393916a..0000000 --- a/app/components/Review/Grid/ImageRenderer/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -import ImageRenderer from "./ImageRenderer"; - -export default ImageRenderer; diff --git a/app/components/Review/Grid/LinkRenderer/LinkRenderer.tsx b/app/components/Review/Grid/LinkRenderer/LinkRenderer.tsx deleted file mode 100644 index c5ff9d6..0000000 --- a/app/components/Review/Grid/LinkRenderer/LinkRenderer.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import { Link } from "@remix-run/react"; -import ExternalLink from "~/components/Icons/ExternalLink"; - -export default function LinkRenderer(params: any) { - return ( -
- - - -
- ); -} diff --git a/app/components/Review/Grid/LinkRenderer/index.ts b/app/components/Review/Grid/LinkRenderer/index.ts deleted file mode 100644 index ecaa4ad..0000000 --- a/app/components/Review/Grid/LinkRenderer/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -import LinkRenderer from "./LinkRenderer"; - -export default LinkRenderer; diff --git a/app/components/Review/Grid/PriceRenderer/PriceRenderer.tsx b/app/components/Review/Grid/PriceRenderer/PriceRenderer.tsx deleted file mode 100644 index db3074c..0000000 --- a/app/components/Review/Grid/PriceRenderer/PriceRenderer.tsx +++ /dev/null @@ -1,7 +0,0 @@ -export default function PriceRenderer(params: any) { - return ( -
- ${params.value} -
- ); -} diff --git a/app/components/Review/Grid/PriceRenderer/index.ts b/app/components/Review/Grid/PriceRenderer/index.ts deleted file mode 100644 index cfef15a..0000000 --- a/app/components/Review/Grid/PriceRenderer/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -import PriceRenderer from "./PriceRenderer"; - -export default PriceRenderer; diff --git a/app/components/Review/Grid/ProofRenderer/ProofRenderer.tsx b/app/components/Review/Grid/ProofRenderer/ProofRenderer.tsx deleted file mode 100644 index ef5e69b..0000000 --- a/app/components/Review/Grid/ProofRenderer/ProofRenderer.tsx +++ /dev/null @@ -1,7 +0,0 @@ -export default function ProofRenderer(params: any) { - return ( -
- {params.value}pf -
- ); -} diff --git a/app/components/Review/Grid/ProofRenderer/index.ts b/app/components/Review/Grid/ProofRenderer/index.ts deleted file mode 100644 index 2d47687..0000000 --- a/app/components/Review/Grid/ProofRenderer/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -import ProofRenderer from "./ProofRenderer"; - -export default ProofRenderer; diff --git a/app/components/Review/Grid/RatingRenderer/RatingRenderer.tsx b/app/components/Review/Grid/RatingRenderer/RatingRenderer.tsx deleted file mode 100644 index 6223b4e..0000000 --- a/app/components/Review/Grid/RatingRenderer/RatingRenderer.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import { classNames } from "~/utils/cssHelper"; - -export default function RatingRenderer(params: any) { - return ( - = 4.0 ? "bg-green-100 text-green-700" : "", - params.value < 4.0 && params.value >= 2.5 - ? "bg-yellow-100 text-yellow-700" - : "null", - params.value < 3 ? "bg-red-100 text-red-700" : "null" - )} - > - {params.value} - - ); -} diff --git a/app/components/Review/Grid/RatingRenderer/index.ts b/app/components/Review/Grid/RatingRenderer/index.ts deleted file mode 100644 index ed1bdd8..0000000 --- a/app/components/Review/Grid/RatingRenderer/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -import RatingRenderer from "./RatingRenderer"; - -export default RatingRenderer; diff --git a/app/components/Review/ReviewHeader/ReviewHeader.tsx b/app/components/Review/ReviewHeader/ReviewHeader.tsx index a5e5a16..f211b26 100644 --- a/app/components/Review/ReviewHeader/ReviewHeader.tsx +++ b/app/components/Review/ReviewHeader/ReviewHeader.tsx @@ -1,12 +1,9 @@ import type { user } from "@prisma/client"; import { Link } from "@remix-run/react"; -import { Ref } from "react"; import { useTypedFetcher } from "remix-typedjson"; -import FollowForm from "~/components/Form/FollowForm"; import AddIcon from "~/components/Icons/AddIcon"; import CalendarDays from "~/components/Icons/CalendarDays"; import ChatBubble from "~/components/Icons/ChatBubble"; -import Plus from "~/components/Icons/Plus"; import User from "~/components/Icons/User"; type ReviewHeaderProps = { diff --git a/app/components/Review/SettingDetails/SettingDetails.tsx b/app/components/Review/SettingDetails/SettingDetails.tsx index 2db3e67..b3f8cb3 100644 --- a/app/components/Review/SettingDetails/SettingDetails.tsx +++ b/app/components/Review/SettingDetails/SettingDetails.tsx @@ -18,7 +18,7 @@ export default function SettingDetails({ review }: SettingDetailsProps) {
-
+
Setting
diff --git a/app/components/Review/WrittenNotes/WrittenNotes.tsx b/app/components/Review/WrittenNotes/WrittenNotes.tsx index 0d684d1..16f54fc 100644 --- a/app/components/Review/WrittenNotes/WrittenNotes.tsx +++ b/app/components/Review/WrittenNotes/WrittenNotes.tsx @@ -8,7 +8,7 @@ export default function WrittenNotes({ review }: IWrittenNotesProps) { return (
-
+
Nose
@@ -16,7 +16,7 @@ export default function WrittenNotes({ review }: IWrittenNotesProps) {
-
+
Palate
@@ -24,7 +24,7 @@ export default function WrittenNotes({ review }: IWrittenNotesProps) {
-
+
Finish
@@ -32,7 +32,7 @@ export default function WrittenNotes({ review }: IWrittenNotesProps) {
-
+
Final Thoughts
diff --git a/app/components/Tables/Bottle/BottleTable.tsx b/app/components/Tables/Bottle/BottleTable.tsx index ad676ac..fcab767 100644 --- a/app/components/Tables/Bottle/BottleTable.tsx +++ b/app/components/Tables/Bottle/BottleTable.tsx @@ -4,12 +4,14 @@ import type { ChangeEvent } from "react"; import type { Limit } from "~/utils/types"; import useDebounce from "~/utils/useDebounce"; import { useTypedFetcher } from "remix-typedjson"; -import GlobalFilter from "~/components/Grids/Common/GlobalFilter"; -import Pagination from "~/components/Grids/Common/Pagination/Pagination"; -import Caption from "~/components/Grids/Common/Caption"; +import GlobalFilter from "../Common/GlobalFilter"; +import Pagination from "../Common/Pagination/Pagination"; +import Caption from "../Common/Caption"; import Body from "../Common/Body/Body"; import Head from "../Common/Head"; import Table from "../Common/Table"; +import { Link } from "@remix-run/react"; +import RightArrowCircle from "~/components/Icons/RightArrowCircle"; export default function BottleTable() { const [currentPage, setCurrentPage] = useState(0); @@ -31,86 +33,89 @@ export default function BottleTable() { scrollable ? setAbleToBeTabbedTo(true) : setAbleToBeTabbedTo(false); }, []); - const columns: Column[] = [ - { - kind: "bottle", - header: "Name", - field: "name", - sort: true, - }, - { - kind: "bottle", - header: "Status", - field: "status", - sort: true, - }, - { - kind: "bottle", - header: "Type", - field: "type", - sort: true, - }, - { - kind: "bottle", - header: "Distiller", - field: "distiller", - sort: true, - }, - { - kind: "bottle", - header: "Producer", - field: "producer", - sort: true, - }, - { - kind: "bottle", - header: "Price", - field: "price", - sort: true, - }, - { - kind: "bottle", - header: "ABV", - field: "alcoholPercent", - sort: true, - }, - { - kind: "bottle", - header: "Proof", - field: "proof", - sort: true, - }, - { - kind: "bottle", - header: "Country", - field: "country", - sort: true, - }, - { - kind: "bottle", - header: "Region", - field: "region", - sort: true, - }, - { - kind: "bottle", - header: "Color", - field: "color", - sort: false, - }, - { - kind: "bottle", - header: "Finishing", - field: "finishing", - sort: false, - }, - { - kind: "bottle", - header: "Size", - field: "size", - sort: false, - }, - ]; + const columns: Column[] = useMemo( + () => [ + { + kind: "bottle", + header: "Name", + field: "name", + sort: true, + }, + { + kind: "bottle", + header: "Status", + field: "status", + sort: true, + }, + { + kind: "bottle", + header: "Type", + field: "type", + sort: true, + }, + { + kind: "bottle", + header: "Distiller", + field: "distiller", + sort: true, + }, + { + kind: "bottle", + header: "Producer", + field: "producer", + sort: true, + }, + { + kind: "bottle", + header: "Price", + field: "price", + sort: true, + }, + { + kind: "bottle", + header: "ABV", + field: "alcoholPercent", + sort: true, + }, + { + kind: "bottle", + header: "Proof", + field: "proof", + sort: true, + }, + { + kind: "bottle", + header: "Country", + field: "country", + sort: true, + }, + { + kind: "bottle", + header: "Region", + field: "region", + sort: true, + }, + { + kind: "bottle", + header: "Color", + field: "color", + sort: false, + }, + { + kind: "bottle", + header: "Finishing", + field: "finishing", + sort: false, + }, + { + kind: "bottle", + header: "Size", + field: "size", + sort: false, + }, + ], + [] + ); const { data, load } = useTypedFetcher>(); const items = useMemo(() => { @@ -155,40 +160,55 @@ export default function BottleTable() { return (
- - -
- + + +
+ ) : ( +
+ +
+

Add your first bottle

{" "} +
+
+ +
+ +
+ )} + - - - +
); } diff --git a/app/components/Tables/Common/Body/Body.tsx b/app/components/Tables/Common/Body/Body.tsx index 3d4d66b..40f84a5 100644 --- a/app/components/Tables/Common/Body/Body.tsx +++ b/app/components/Tables/Common/Body/Body.tsx @@ -1,33 +1,86 @@ -import type { GridBottle } from "~/utils/types"; +import type { GridBottle, GridItem, GridReview } from "~/utils/types"; import NameCell from "../NameCell"; import StatusCell from "../StatusCell"; +import { useMemo } from "react"; +import { Link } from "@remix-run/react"; +import ExternalLink from "~/components/Icons/ExternalLink"; type BodyProps = { - items: GridBottle[] | []; + items: GridBottle[] | GridReview[] | []; }; export default function Body({ items }: BodyProps) { + const isReview = useMemo(() => { + return (item: GridItem): item is GridReview => { + return (item as GridReview).date !== undefined; + }; + }, []); + return ( - {items.map((item) => ( - - - - {item.type} - {item.distiller} - {item.producer} - ${item.price} - - {item.alcoholPercent}% - - {item.proof}pf - {item.country} - {item.region} - {item.color} - {item.finishing} - {item.size} - - ))} + {items.map((item) => { + return isReview(item) ? ( + + + {item.bottle?.type} + {item.date} + + {item.bottle?.distiller} + + + {item.bottle?.producer} + + + ${item.bottle?.price} + + + {item.bottle?.alcoholPercent}% + + + {item.bottle?.proof}pf + + {item.value} + + {item.overallRating} + + + + + + + + ) : ( + + + + {item.type} + {item.distiller} + {item.producer} + ${item.price} + + {item.alcoholPercent}% + + {item.proof}pf + {item.country} + {item.region} + {item.color} + {item.finishing} + {item.size} + + ); + })} ); } + +/* + +*/ diff --git a/app/components/Grids/Common/Caption/Caption.tsx b/app/components/Tables/Common/Caption/Caption.tsx similarity index 100% rename from app/components/Grids/Common/Caption/Caption.tsx rename to app/components/Tables/Common/Caption/Caption.tsx diff --git a/app/components/Grids/Common/Caption/index.ts b/app/components/Tables/Common/Caption/index.ts similarity index 100% rename from app/components/Grids/Common/Caption/index.ts rename to app/components/Tables/Common/Caption/index.ts diff --git a/app/components/Tables/Common/DataRow/DataRow.tsx b/app/components/Tables/Common/DataRow/DataRow.tsx index 51b76b8..7be4934 100644 --- a/app/components/Tables/Common/DataRow/DataRow.tsx +++ b/app/components/Tables/Common/DataRow/DataRow.tsx @@ -1,4 +1,4 @@ -import type { GridBottle } from "~/models/bottle.server"; +import type { GridBottle } from "~/utils/types"; import DataCell from "../DataCell"; import NameCell from "../NameCell"; import StatusCell from "../StatusCell"; @@ -11,13 +11,12 @@ type DataRowProps = { export default function DataRow({ item }: DataRowProps) { return ( - + - diff --git a/app/components/Grids/Common/GlobalFilter/GlobalFilter.tsx b/app/components/Tables/Common/GlobalFilter/GlobalFilter.tsx similarity index 85% rename from app/components/Grids/Common/GlobalFilter/GlobalFilter.tsx rename to app/components/Tables/Common/GlobalFilter/GlobalFilter.tsx index ac27ff8..52aa5d7 100644 --- a/app/components/Grids/Common/GlobalFilter/GlobalFilter.tsx +++ b/app/components/Tables/Common/GlobalFilter/GlobalFilter.tsx @@ -1,5 +1,5 @@ import type { ChangeEvent, Dispatch, SetStateAction } from "react"; -import type { Limit } from "../../TestGrid/NewTestGrid"; +import type { Limit } from "~/utils/types"; import PageLimit from "../PageLimit/PageLimit"; type GlobalFilterProps = { @@ -16,12 +16,12 @@ export default function Filter({ setLimit, }: GlobalFilterProps) { return ( -
+
Search: void; - sort: Sort; + sort: boolean; }; export default function Head({ diff --git a/app/components/Tables/Common/NewHeaderCell/HeaderCell.tsx b/app/components/Tables/Common/HeaderCell/HeaderCell.tsx similarity index 92% rename from app/components/Tables/Common/NewHeaderCell/HeaderCell.tsx rename to app/components/Tables/Common/HeaderCell/HeaderCell.tsx index 548968c..f8b4e07 100644 --- a/app/components/Tables/Common/NewHeaderCell/HeaderCell.tsx +++ b/app/components/Tables/Common/HeaderCell/HeaderCell.tsx @@ -4,7 +4,7 @@ import type { Column, SortFields, Sort } from "~/utils/types"; type HeaderCellProps = { column: Column; index: number; - sort: Sort; + sort: boolean; handleSortingChange: (field: SortFields) => void; classes?: string; }; @@ -27,7 +27,7 @@ export default function HeaderCell({ >
{column.header}
-
+
{column.sort ? : null}
diff --git a/app/components/Tables/Common/NewHeaderCell/index.ts b/app/components/Tables/Common/HeaderCell/index.ts similarity index 100% rename from app/components/Tables/Common/NewHeaderCell/index.ts rename to app/components/Tables/Common/HeaderCell/index.ts diff --git a/app/components/Tables/Common/HeaderRow/HeaderRow.tsx b/app/components/Tables/Common/HeaderRow/HeaderRow.tsx index e6fa259..04339e6 100644 --- a/app/components/Tables/Common/HeaderRow/HeaderRow.tsx +++ b/app/components/Tables/Common/HeaderRow/HeaderRow.tsx @@ -1,5 +1,5 @@ import type { Column, SortFields } from "~/utils/types"; -import HeaderCell from "../NewHeaderCell"; +import HeaderCell from "../HeaderCell"; import NameHeader from "../NameHeader/NameHeader"; type HeaderRowProps = { @@ -18,6 +18,7 @@ export default function HeaderRow({ handleSortingChange={handleSortingChange} /> void; +}; + +export default function NameHeader({ + column, + handleSortingChange, +}: NameHeaderProps) { + return ( + handleSortingChange(column.field as SortFields)} + className="border-1 sticky left-0 z-10 min-w-[300px] border-gray-100 bg-blue-500 px-4 py-6 text-left text-sm uppercase text-white" + > +
+
+
{column.header}
+
+
+ + +
+
+ + ); +} diff --git a/app/components/Grids/Common/PageLimit/PageLimit.tsx b/app/components/Tables/Common/PageLimit/PageLimit.tsx similarity index 97% rename from app/components/Grids/Common/PageLimit/PageLimit.tsx rename to app/components/Tables/Common/PageLimit/PageLimit.tsx index 1dbd96c..0402afd 100644 --- a/app/components/Grids/Common/PageLimit/PageLimit.tsx +++ b/app/components/Tables/Common/PageLimit/PageLimit.tsx @@ -1,4 +1,4 @@ -import type { Limit } from "../../TestGrid/NewTestGrid"; +import type { Limit } from "~/utils/types"; import { Fragment } from "react"; import { Listbox, Transition } from "@headlessui/react"; import ChevronDown from "~/components/Icons/ChevronDown"; diff --git a/app/components/Grids/Common/Pagination/Pagination.tsx b/app/components/Tables/Common/Pagination/Pagination.tsx similarity index 94% rename from app/components/Grids/Common/Pagination/Pagination.tsx rename to app/components/Tables/Common/Pagination/Pagination.tsx index c813539..aebab0c 100644 --- a/app/components/Grids/Common/Pagination/Pagination.tsx +++ b/app/components/Tables/Common/Pagination/Pagination.tsx @@ -16,7 +16,7 @@ export default function Pagination({ setCurrentPage, }: PaginationProps) { return ( -
+
Total Results: {totalItems} diff --git a/app/components/Tables/Common/SortIcon/SortIcon.tsx b/app/components/Tables/Common/SortIcon/SortIcon.tsx index 504a567..557ba36 100644 --- a/app/components/Tables/Common/SortIcon/SortIcon.tsx +++ b/app/components/Tables/Common/SortIcon/SortIcon.tsx @@ -1,21 +1,31 @@ +import Sort from "~/components/Icons/Sort"; +import SortDown from "~/components/Icons/SortDown"; +import SortUp from "~/components/Icons/SortUp"; import type { Column, SortDirection, SortFields } from "~/utils/types"; type SortIconProps = { - sort: { - field: SortFields; - direction: SortDirection; - }; + sort: boolean; column: Column; }; export default function SortIcon({ sort, column }: SortIconProps) { return ( - - {column.field === sort.field - ? sort.direction === "asc" - ? "↑" - : "↓" - : "️↕"} - +
+ + + +
); } + +/* +{ {column.field === sort.field ? ( + sort.direction === "asc" ? ( + + ) : ( + + ) + ) : ( + + )} } +*/ diff --git a/app/components/Tables/Common/Table/Table.tsx b/app/components/Tables/Common/Table/Table.tsx index 0428731..e67c245 100644 --- a/app/components/Tables/Common/Table/Table.tsx +++ b/app/components/Tables/Common/Table/Table.tsx @@ -24,7 +24,7 @@ export default function Table({ ref={tableRef} > >; -}; - -type Limit = 10 | 25 | 50 | 75 | 100 | 250; - -export default function Pagination({ data, setData }: PaginationProps) { - const [loading, setLoading] = useState(false); - const [limit, setLimit] = useState(10); - const [currentPage, setCurrentPage] = useState(0); - const searchFetcher = useTypedFetcher(); -} diff --git a/app/components/Tables/Pagination/Pill.tsx b/app/components/Tables/Pagination/Pill.tsx deleted file mode 100644 index dbb8c3f..0000000 --- a/app/components/Tables/Pagination/Pill.tsx +++ /dev/null @@ -1,15 +0,0 @@ -type PillProps = { - character: string; - disabled: boolean; - onClick: () => void; -}; - -export default function Pill({ character, disabled, onClick }: PillProps) { - return ( -
  • - -
  • - ); -} diff --git a/app/components/Tables/Review/ReviewTable.tsx b/app/components/Tables/Review/ReviewTable.tsx index e1e1389..5a40cdc 100644 --- a/app/components/Tables/Review/ReviewTable.tsx +++ b/app/components/Tables/Review/ReviewTable.tsx @@ -1,25 +1,203 @@ -import DataRow from "../Common/DataRow"; +import type { GridReview, Column, TableData, Sort, Limit } from "~/utils/types"; import Head from "../Common/Head"; +import GlobalFilter from "../Common/GlobalFilter"; +import { + type ChangeEvent, + useCallback, + useEffect, + useMemo, + useRef, + useState, +} from "react"; +import useDebounce from "~/utils/useDebounce"; +import { useTypedFetcher } from "remix-typedjson"; +import Table from "../Common/Table"; +import Caption from "../Common/Caption"; +import Body from "../Common/Body"; +import { Link } from "@remix-run/react"; +import RightArrowCircle from "~/components/Icons/RightArrowCircle"; +import Pagination from "../Common/Pagination/Pagination"; -type ReviewTableProps = { - columns: Columns[]; - items: GridReview[] | []; -}; +export default function ReviewTable() { + const [currentPage, setCurrentPage] = useState(0); + const [query, setQuery] = useState(""); + const [limit, setLimit] = useState(10); + const searchTerm = useDebounce(query, 300); + const [sort, setSort] = useState({ + field: "name", + direction: "asc", + }); + + const [ableToBeTabbedTo, setAbleToBeTabbedTo] = useState(false); + const tableRef = useRef(null); + + useEffect(() => { + const tableContainer = tableRef?.current; + let scrollable = + tableContainer?.scrollWidth! > tableContainer?.clientWidth!; + scrollable ? setAbleToBeTabbedTo(true) : setAbleToBeTabbedTo(false); + }, []); + + const columns: Column[] = useMemo( + () => [ + { + kind: "review", + header: "Name", + field: "name", + sort: true, + }, + { + kind: "review", + header: "Type", + field: "type", + sort: true, + }, + { + kind: "review", + header: "Distiller", + field: "distiller", + sort: true, + }, + { + kind: "review", + header: "Producer", + field: "producer", + sort: true, + }, + { + kind: "review", + header: "ABV", + field: "alcoholPercent", + sort: true, + }, + { + kind: "review", + header: "Proof", + field: "proof", + sort: true, + }, + { + kind: "review", + header: "Price", + field: "price", + sort: true, + }, + { + kind: "review", + header: "Value", + field: "value", + sort: true, + }, + { + kind: "review", + header: "Rating", + field: "overallRating", + sort: true, + }, + { + kind: "review", + header: "Date", + field: "date", + sort: true, + }, + { + kind: "review", + header: "Link", + field: "link", + sort: false, + }, + ], + [] + ); + + const { data, load } = useTypedFetcher>(); + const items = useMemo(() => { + return data?.items; + }, [data]); + + const totalReviews = data?.totalItems; + const totalPages = data?.totalPages; + + const getInitialData = useCallback(() => { + load(`/services/search/review/fetch?page=0&limit=${limit}`); + }, [load, limit]); + + useEffect(() => { + getInitialData(); + }, [getInitialData]); + + const reloadData = useCallback(() => { + load( + `/services/search/review/fetch?page=${currentPage}&query=${searchTerm}&limit=${limit}&sort=${sort.field}&direction=${sort.direction}` + ); + }, [load, limit, currentPage, searchTerm, sort.field, sort.direction]); + + useEffect(() => { + reloadData(); + }, [reloadData]); + + const onFirst = () => setCurrentPage(0); + const onLast = () => setCurrentPage(totalPages! - 1); + const handleQueryChange = (e: ChangeEvent) => + setQuery(e.target.value); + + const handleSortingChange = (field: Sort["field"]) => { + setSort((prev) => ({ + field, + direction: + prev.direction === "asc" && prev.field === field ? "desc" : "asc", + })); + }; -export default function ReviewTable({ columns, items }: ReviewTableProps) { return ( -
    -
    - - - {items.map((review) => ( - - ))} - -
    +
    + <> + + {items.length > 0 ? ( + +
    + + +
    + ) : ( +
    + +
    +

    Add your first review

    {" "} +
    +
    + +
    + +
    + )} + +
    ); } diff --git a/app/components/UI/Button/Button.stories.tsx b/app/components/UI/Button/Button.stories.tsx new file mode 100644 index 0000000..3e9713f --- /dev/null +++ b/app/components/UI/Button/Button.stories.tsx @@ -0,0 +1,51 @@ +import { unstable_createRemixStub as createRemixStub } from "@remix-run/testing"; +import type { Meta, StoryObj } from "@storybook/react"; +import Button from "./Button"; + +const meta: Meta = { + title: "components/UI/Button", + component: Button, + decorators: [ + (Story) => { + const RemixStub = createRemixStub([{ path: "/", element: }]); + return ; + }, + ], + tags: ["autodocs"], + args: { + callToAction: "Submit", + type: "submit", + primary: true, + disabled: false, + }, +}; + +export default meta; + +type Story = StoryObj; + +export const Primary: Story = {}; + +export const Secondary: Story = { + args: { + primary: false, + }, +}; + +export const Disabled: Story = { + args: { + disabled: true, + }, +}; + +export const CenterAligned: Story = { + args: { + position: "middle", + }, +}; + +export const RightAligned: Story = { + args: { + position: "right", + }, +}; diff --git a/app/components/UI/Button/Button.tsx b/app/components/UI/Button/Button.tsx index e08ab6b..2e1c7cd 100644 --- a/app/components/UI/Button/Button.tsx +++ b/app/components/UI/Button/Button.tsx @@ -1,5 +1,3 @@ -import * as React from "react"; - export interface ButtonProps extends React.HTMLAttributes { callToAction: string; primary?: boolean; @@ -7,27 +5,41 @@ export interface ButtonProps extends React.HTMLAttributes { disabled?: boolean; name?: string; value?: string; + position?: "left" | "middle" | "right"; } export default function Button({ callToAction, type, primary, - disabled = false, + disabled, name, value, + position, }: ButtonProps) { return ( -
    +