bit of work on the form component
This commit is contained in:
@@ -54,11 +54,13 @@ const Friends: FC<{ targetUser?: IUser }> = ({ targetUser }) => {
|
||||
<Card extraStyles="flex-row">
|
||||
<h2>Friends ({ userList?.length ?? "0" }):</h2>
|
||||
|
||||
<div className="friends-list">
|
||||
{
|
||||
userList.map((user: IUser) => {
|
||||
return <UserCard key={v4()} targetUser={user} />
|
||||
})
|
||||
}
|
||||
</div>
|
||||
|
||||
<aside>
|
||||
<p>Looking for someone else?</p>
|
||||
|
||||
@@ -63,7 +63,7 @@ const AddRecipe = () => {
|
||||
parent: "AddRecipe",
|
||||
keys: ["name", "preptime", "course", "cuisine", "ingredients", "description"],
|
||||
labels: ["Recipe Name:", "Prep Time:", "Course:", "Cuisine:", "Ingredients:", "Description:"],
|
||||
dataTypes: ['text', 'text', 'custom picker', 'custom picker', 'custom picker', 'TINYMCE'],
|
||||
dataTypes: ['text', 'text', 'custom picker', 'custom picker', 'SELECTOR', 'TINYMCE'],
|
||||
initialState: input,
|
||||
getState: getFormState,
|
||||
richTextInitialValue: "<p>Enter recipe details here!</p>"
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { FC } from "react"
|
||||
import { MultiChildPortal } from "../../util/types"
|
||||
import { FC } from "react";
|
||||
|
||||
const Card: FC<MultiChildPortal> = ({ children = <></>, extraStyles = ""}) => {
|
||||
|
||||
const Card: FC<{ children?: JSX.Element | JSX.Element[], extraStyles?: string }> = ({ children = <></>, extraStyles = ""}) => {
|
||||
return (
|
||||
<div className={`ui-card ${extraStyles}`}>
|
||||
{ children }
|
||||
{ Array.isArray(children) ? <>{children}</> : children }
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import { ChangeEvent, FC, useEffect, useState } from "react"
|
||||
import { v4 } from "uuid"
|
||||
import { useAuthContext } from "../../context/AuthContext";
|
||||
import { IIngredient, IUser } from "../../schemas";
|
||||
import API from "../../util/API";
|
||||
import RichText from "./RichText"
|
||||
import Selector from "./Selector";
|
||||
import "/src/sass/components/Form.scss";
|
||||
|
||||
export interface FormConfig<T> {
|
||||
@@ -27,6 +31,8 @@ const Form: FC<FormProps> = ({ parent, _config }) => {
|
||||
const [state, setState] = useState<T>();
|
||||
const [contents, setContents] = useState<JSX.Element[]>();
|
||||
|
||||
const { token } = useAuthContext();
|
||||
|
||||
// initial setup
|
||||
useEffect(() => {
|
||||
if (!config) setConfig({
|
||||
@@ -66,35 +72,61 @@ const Form: FC<FormProps> = ({ parent, _config }) => {
|
||||
})
|
||||
}
|
||||
|
||||
async function populateSelector(key: string): Promise<any[] | null> {
|
||||
if (!token) return null;
|
||||
|
||||
switch (key) {
|
||||
case "ingredient":
|
||||
const ingredients = new API.Ingredient(token);
|
||||
const result = await ingredients.getAll();
|
||||
if (result) return result;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// mount the form once config has been loaded
|
||||
useEffect(() => {
|
||||
if (state && config) {
|
||||
const result = config.keys.map((each: string, i: number) => {
|
||||
(async() => {
|
||||
const result = config.keys.map(async (each: string, i: number) => {
|
||||
if (config.dataTypes![i] == 'TINYMCE') {
|
||||
return (
|
||||
<div className="form-row-editor" id={`${config.parent}-row-${i}`} key={v4()}>
|
||||
<label htmlFor={`${config.parent}-${each}`}>{config.labels![i]}</label>
|
||||
<RichText id={`${config.parent}-${each}`} initialValue={config.richTextInitialValue} getState={(txt) => updateRichText(txt, i)} />
|
||||
</div>
|
||||
)
|
||||
} else if (config.dataTypes![i] == 'SELECTOR') {
|
||||
type StrongType = Partial<T> & { id: number, name: string };
|
||||
const storedResult = await (async() => {
|
||||
const result = await populateSelector(config?.labels![i] || "");
|
||||
if (result) return result as T[];
|
||||
return null;
|
||||
})();
|
||||
|
||||
if (config.dataTypes![i] == 'TINYMCE') {
|
||||
return (
|
||||
<div className="form-row-editor" id={`${config.parent}-row-${i}`} key={v4()}>
|
||||
<label htmlFor={`${config.parent}-${each}`}>{config.labels![i]}</label>
|
||||
<RichText id={`${config.parent}-${each}`} initialValue={config.richTextInitialValue} getState={(txt) => updateRichText(txt, i)} />
|
||||
</div>
|
||||
)
|
||||
} else {
|
||||
return (
|
||||
<div className="form-row" id={`${config.parent}-row-${i}`} key={v4()}>
|
||||
<label htmlFor={`${config.parent}-${each}`}>{config.labels![i]}</label>
|
||||
<input
|
||||
type={config.dataTypes![i]}
|
||||
id={`${config.parent}-${each}`}
|
||||
onChange={(e) => update(e, i)}
|
||||
value={state[i as keyof T] as string}>
|
||||
</input>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
});
|
||||
|
||||
setContents(result);
|
||||
return <Selector<StrongType> optionList={storedResult || []} />
|
||||
} else {
|
||||
return (
|
||||
<div className="form-row" id={`${config.parent}-row-${i}`} key={v4()}>
|
||||
<label htmlFor={`${config.parent}-${each}`}>{config.labels![i]}</label>
|
||||
<input
|
||||
type={config.dataTypes![i]}
|
||||
id={`${config.parent}-${each}`}
|
||||
onChange={(e) => update(e, i)}
|
||||
value={state[i as keyof T] as string}>
|
||||
</input>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
});
|
||||
|
||||
const mappedContents = await Promise.all(result);
|
||||
mappedContents && setContents(mappedContents);
|
||||
})();
|
||||
}
|
||||
}, [config]);
|
||||
|
||||
|
||||
17
client/src/components/ui/Selector.tsx
Normal file
17
client/src/components/ui/Selector.tsx
Normal file
@@ -0,0 +1,17 @@
|
||||
interface Entity {
|
||||
id: string | number
|
||||
name?: string
|
||||
}
|
||||
|
||||
function Selector<T extends Entity>({ optionList }: { optionList: Array<T> }) {
|
||||
// const Selector: FC<{ optionList: Array<T extends HasID> }> = ({ optionList }) => {
|
||||
return (
|
||||
<select className="ui-select-component">
|
||||
{ optionList.map(item =>
|
||||
<option id={`select-item-${item.name}-${item.id}`}>{item.name}</option>
|
||||
)}
|
||||
</select>
|
||||
)
|
||||
}
|
||||
|
||||
export default Selector
|
||||
@@ -46,10 +46,12 @@ const UserCard: UserCardType = ({ extraStyles, targetUser }) => {
|
||||
|
||||
return (
|
||||
<Card extraStyles={'user-card' + extraStyles}>
|
||||
<>
|
||||
<div className="avatar"></div>
|
||||
<h3><a href={`/profile?id=${targetUser.id}`}>{targetUser.firstname} {targetUser.lastname.substring(0,1)}.</a></h3>
|
||||
<h4>@{targetUser.handle}</h4>
|
||||
{ buttonVariant }
|
||||
</>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ module API {
|
||||
};
|
||||
}
|
||||
|
||||
async customRoute(method: CRUDMETHOD, path: string, data?: any, requireHeaders = true) {
|
||||
protected async customRoute(method: CRUDMETHOD, path: string, data?: any, requireHeaders = true) {
|
||||
switch (method) {
|
||||
case CRUDMETHOD.GET:
|
||||
return this.instance.get(this.endpoint + path, (requireHeaders && this.headers));
|
||||
@@ -186,7 +186,6 @@ module API {
|
||||
|
||||
export class Ingredient extends RestController<IIngredient> {
|
||||
constructor(token: string) {
|
||||
if (!token) throw new Error("Missing required token");
|
||||
super(Settings.getAPISTRING() + "/app/ingredients", token);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,8 +20,6 @@ export interface ProtectParams extends PortalBase {
|
||||
|
||||
interface UserCardProps extends PortalBase {
|
||||
targetUser: IUser
|
||||
canAdd?: boolean
|
||||
liftData?: (data: any) => void
|
||||
}
|
||||
|
||||
interface NavbarProps {
|
||||
|
||||
Reference in New Issue
Block a user