From a43ef624352a4d9f8e08e2c4753e5b609609f28f Mon Sep 17 00:00:00 2001 From: Matthew Penner Date: Fri, 8 Jan 2021 10:48:11 -0700 Subject: [PATCH] admin(ui): show eggs in NestEditContainer --- .../admin/nests/NestEditContainer.tsx | 134 ++++++++++++++++-- .../components/admin/roles/RolesContainer.tsx | 2 +- .../scripts/components/elements/Input.tsx | 20 +-- 3 files changed, 132 insertions(+), 24 deletions(-) diff --git a/resources/scripts/components/admin/nests/NestEditContainer.tsx b/resources/scripts/components/admin/nests/NestEditContainer.tsx index 40760f328..518983fd3 100644 --- a/resources/scripts/components/admin/nests/NestEditContainer.tsx +++ b/resources/scripts/components/admin/nests/NestEditContainer.tsx @@ -1,8 +1,5 @@ -import CopyOnClick from '@/components/elements/CopyOnClick'; -import Input from '@/components/elements/Input'; -import Label from '@/components/elements/Label'; -import React, { createContext, useContext, useEffect, useState } from 'react'; -import { useRouteMatch } from 'react-router-dom'; +import React, { useEffect, useState } from 'react'; +import { NavLink, useRouteMatch } from 'react-router-dom'; import tw from 'twin.macro'; import AdminContentBlock from '@/components/admin/AdminContentBlock'; import Spinner from '@/components/elements/Spinner'; @@ -15,16 +12,47 @@ import Button from '@/components/elements/Button'; import Field from '@/components/elements/Field'; import SpinnerOverlay from '@/components/elements/SpinnerOverlay'; import { ApplicationStore } from '@/state'; -import { Actions, useStoreActions } from 'easy-peasy'; +import { action, Action, Actions, createContextStore, useStoreActions } from 'easy-peasy'; import { Form, Formik, FormikHelpers } from 'formik'; import AdminBox from '@/components/admin/AdminBox'; +import AdminCheckbox from '@/components/admin/AdminCheckbox'; +import AdminTable, { ContentWrapper, NoItems, TableBody, TableHead, TableHeader, TableRow } from '@/components/admin/AdminTable'; +import CopyOnClick from '@/components/elements/CopyOnClick'; +import Input from '@/components/elements/Input'; +import Label from '@/components/elements/Label'; interface ctx { nest: Nest | undefined; - setNest: (value: Nest | undefined) => void; + setNest: Action; + + selectedEggs: number[]; + + setSelectedEggs: Action; + appendSelectedEggs: Action; + removeSelectedEggs: Action; } -export const Context = createContext({ nest: undefined, setNest: () => 1 }); +export const Context = createContextStore({ + nest: undefined, + + setNest: action((state, payload) => { + state.nest = payload; + }), + + selectedEggs: [], + + setSelectedEggs: action((state, payload) => { + state.selectedEggs = payload; + }), + + appendSelectedEggs: action((state, payload) => { + state.selectedEggs = state.selectedEggs.filter(id => id !== payload).concat(payload); + }), + + removeSelectedEggs: action((state, payload) => { + state.selectedEggs = state.selectedEggs.filter(id => id !== payload); + }), +}); interface Values { name: string; @@ -33,7 +61,8 @@ interface Values { const EditInformationContainer = () => { const { clearFlashes, clearAndAddHttpError } = useStoreActions((actions: Actions) => actions.flashes); - const { nest, setNest } = useContext(Context); + const nest = Context.useStoreState(state => state.nest); + const setNest = Context.useStoreActions(actions => actions.setNest); if (nest === undefined) { return ( @@ -105,7 +134,7 @@ const EditInformationContainer = () => { }; const ViewDetailsContainer = () => { - const { nest } = useContext(Context); + const nest = Context.useStoreState(state => state.nest); if (nest === undefined) { return ( @@ -155,13 +184,37 @@ const ViewDetailsContainer = () => { ); }; +const RowCheckbox = ({ id }: { id: number }) => { + const isChecked = Context.useStoreState(state => state.selectedEggs.indexOf(id) >= 0); + const appendSelectedEggs = Context.useStoreActions(actions => actions.appendSelectedEggs); + const removeSelectedEggs = Context.useStoreActions(actions => actions.removeSelectedEggs); + + return ( + ) => { + if (e.currentTarget.checked) { + appendSelectedEggs(id); + } else { + removeSelectedEggs(id); + } + }} + /> + ); +}; + const NestEditContainer = () => { const match = useRouteMatch<{ nestId?: string }>(); const { clearFlashes, clearAndAddHttpError } = useStoreActions((actions: Actions) => actions.flashes); const [ loading, setLoading ] = useState(true); - const { nest, setNest } = useContext(Context); + const nest = Context.useStoreState(state => state.nest); + const setNest = Context.useStoreActions(actions => actions.setNest); + + const setSelectedEggs = Context.useStoreActions(actions => actions.setSelectedEggs); + const selectedEggsLength = Context.useStoreState(state => state.selectedEggs.length); useEffect(() => { clearFlashes('nest'); @@ -187,6 +240,12 @@ const NestEditContainer = () => { ); } + const length = nest.relations.eggs?.length || 0; + + const onSelectAllClick = (e: React.ChangeEvent) => { + setSelectedEggs(e.currentTarget.checked ? (nest.relations.eggs?.map(egg => egg.id) || []) : []); + }; + return (
@@ -198,19 +257,64 @@ const NestEditContainer = () => { -
+
+ + + { length < 1 ? + + : + +
+ + + + + + + + + { + nest.relations.eggs?.map(egg => ( + + + + + + + + + + )) + } + +
+ + + + {egg.id} + + + + {egg.name} + + {egg.description}
+
+
+ } +
); }; export default () => { - const [ nest, setNest ] = useState(undefined); - return ( - + ); diff --git a/resources/scripts/components/admin/roles/RolesContainer.tsx b/resources/scripts/components/admin/roles/RolesContainer.tsx index 9c87a348d..41066dfb8 100644 --- a/resources/scripts/components/admin/roles/RolesContainer.tsx +++ b/resources/scripts/components/admin/roles/RolesContainer.tsx @@ -12,7 +12,7 @@ import AdminCheckbox from '@/components/admin/AdminCheckbox'; import AdminTable, { ContentWrapper, Loading, NoItems, TableBody, TableHead, TableHeader, TableRow } from '@/components/admin/AdminTable'; import CopyOnClick from '@/components/elements/CopyOnClick'; -const RowCheckbox = ({ id }: { id: number}) => { +const RowCheckbox = ({ id }: { id: number }) => { const isChecked = AdminContext.useStoreState(state => state.roles.selectedRoles.indexOf(id) >= 0); const appendSelectedRole = AdminContext.useStoreActions(actions => actions.roles.appendSelectedRole); const removeSelectedRole = AdminContext.useStoreActions(actions => actions.roles.removeSelectedRole); diff --git a/resources/scripts/components/elements/Input.tsx b/resources/scripts/components/elements/Input.tsx index 21845d5b0..7e71f6384 100644 --- a/resources/scripts/components/elements/Input.tsx +++ b/resources/scripts/components/elements/Input.tsx @@ -7,9 +7,9 @@ export interface Props { } const light = css` - ${tw`bg-white border-neutral-200 text-neutral-800`}; + ${tw`bg-white border-neutral-200 text-neutral-800`}; &:focus { ${tw`border-primary-400`} } - + &:disabled { ${tw`bg-neutral-100 border-neutral-200`}; } @@ -40,16 +40,16 @@ const inputStyle = css` ${tw`appearance-none outline-none w-full min-w-0`}; ${tw`p-3 border-2 rounded text-sm transition-all duration-150`}; ${tw`bg-neutral-600 border-neutral-500 hover:border-neutral-400 text-neutral-200 shadow-none focus:ring-0`}; - + & + .input-help { ${tw`mt-1 text-xs`}; ${props => props.hasError ? tw`text-red-200` : tw`text-neutral-200`}; } - + &:required, &:invalid { ${tw`shadow-none`}; } - + &:not(:disabled):not(:read-only):focus { ${tw`shadow-md border-primary-300 ring-2 ring-primary-400 ring-opacity-50`}; ${props => props.hasError && tw`border-red-300 ring-red-200`}; @@ -58,7 +58,11 @@ const inputStyle = css` &:disabled { ${tw`opacity-75`}; } - + + &:read-only { + ${tw`border-neutral-800 bg-neutral-900`}; + } + ${props => props.isLight && light}; ${props => props.hasError && tw`text-red-100 border-red-400 hover:border-red-300`}; `; @@ -67,10 +71,10 @@ const Input = styled.input` &:not([type="checkbox"]):not([type="radio"]) { ${inputStyle}; } - + &[type="checkbox"], &[type="radio"] { ${checkboxStyle}; - + &[type="radio"] { ${tw`rounded-full`}; }