[portal][ui] add companies filter

pull/342/head
Yangshun Tay 2 years ago
parent e7d08d46c8
commit ebacebb26b

@ -0,0 +1,40 @@
import { useState } from 'react';
import type { TypeaheadOption } from '@tih/ui';
import { Typeahead } from '@tih/ui';
import { trpc } from '~/utils/trpc';
type Props = Readonly<{
disabled?: boolean;
onSelect: (option: TypeaheadOption) => void;
}>;
export default function CompaniesTypeahead({ disabled, onSelect }: Props) {
const [query, setQuery] = useState('');
const companies = trpc.useQuery([
'companies.list',
{
name: query,
},
]);
const { data } = companies;
return (
<Typeahead
disabled={disabled}
label="Company"
noResultsMessage="No companies found"
nullable={true}
options={
data?.map(({ id, name }) => ({
id,
label: name,
value: id,
})) ?? []
}
onQueryChange={setQuery}
onSelect={onSelect}
/>
);
}

@ -1,11 +1,23 @@
import { useState } from 'react';
import type { TypeaheadOption } from '@tih/ui';
import CompaniesTypeahead from '~/components/global/CompaniesTypeahead';
export default function HomePage() { export default function HomePage() {
const [selectedCompany, setSelectedCompany] =
useState<TypeaheadOption | null>(null);
return ( return (
<main className="flex-1 overflow-y-auto"> <main className="flex-1 overflow-y-auto">
<div className="flex h-full items-center justify-center"> <div className="flex h-full items-center justify-center">
<div> <div className="space-y-4">
<h1 className="text-primary-600 text-center text-4xl font-bold"> <h1 className="text-primary-600 text-center text-4xl font-bold">
Homepage Homepage
</h1> </h1>
<CompaniesTypeahead
onSelect={(option) => setSelectedCompany(option)}
/>
<pre>{JSON.stringify(selectedCompany, null, 2)}</pre>
</div> </div>
</div> </div>
</main> </main>

@ -0,0 +1,23 @@
import { z } from 'zod';
import { createRouter } from './context';
export const companiesRouter = createRouter().query('list', {
input: z.object({
name: z.string(),
}),
async resolve({ ctx, input }) {
return await ctx.prisma.company.findMany({
orderBy: {
name: 'desc',
},
take: 10,
where: {
name: {
contains: input.name,
mode: 'insensitive',
},
},
});
},
});

@ -1,5 +1,6 @@
import superjson from 'superjson'; import superjson from 'superjson';
import { companiesRouter } from './companies-router';
import { createRouter } from './context'; import { createRouter } from './context';
import { protectedExampleRouter } from './protected-example-router'; import { protectedExampleRouter } from './protected-example-router';
import { questionsQuestionRouter } from './questions-question-router'; import { questionsQuestionRouter } from './questions-question-router';
@ -19,6 +20,7 @@ export const appRouter = createRouter()
.merge('auth.', protectedExampleRouter) .merge('auth.', protectedExampleRouter)
.merge('todos.', todosRouter) .merge('todos.', todosRouter)
.merge('todos.user.', todosUserRouter) .merge('todos.user.', todosUserRouter)
.merge('companies.', companiesRouter)
.merge('resumes.resume.', resumesRouter) .merge('resumes.resume.', resumesRouter)
.merge('resumes.resume.user.', resumesResumeUserRouter) .merge('resumes.resume.user.', resumesResumeUserRouter)
.merge('resumes.star.user.', resumesStarUserRouter) .merge('resumes.star.user.', resumesStarUserRouter)

@ -14,6 +14,9 @@ export default {
label: { label: {
control: 'text', control: 'text',
}, },
noResultsMessage: {
control: 'text',
},
}, },
component: Typeahead, component: Typeahead,
parameters: { parameters: {
@ -62,9 +65,9 @@ export function Basic({
isLabelHidden={isLabelHidden} isLabelHidden={isLabelHidden}
label={label} label={label}
options={filteredPeople} options={filteredPeople}
selectedOption={selectedEntry} value={selectedEntry}
onQueryChange={setQuery} onQueryChange={setQuery}
onSelectOption={setSelectedEntry} onSelect={setSelectedEntry}
/> />
); );
} }

@ -14,30 +14,51 @@ type Props = Readonly<{
disabled?: boolean; disabled?: boolean;
isLabelHidden?: boolean; isLabelHidden?: boolean;
label: string; label: string;
noResultsMessage?: string;
nullable?: boolean;
onQueryChange: ( onQueryChange: (
value: string, value: string,
event: React.ChangeEvent<HTMLInputElement>, event: React.ChangeEvent<HTMLInputElement>,
) => void; ) => void;
onSelectOption: (option: TypeaheadOption) => void; onSelect: (option: TypeaheadOption) => void;
options: ReadonlyArray<TypeaheadOption>; options: ReadonlyArray<TypeaheadOption>;
selectedOption: TypeaheadOption; value?: TypeaheadOption;
}>; }>;
export default function Typeahead({ export default function Typeahead({
disabled = false, disabled = false,
isLabelHidden, isLabelHidden,
label, label,
noResultsMessage = 'No results',
nullable = false,
options, options,
onQueryChange, onQueryChange,
selectedOption, value,
onSelectOption, onSelect,
}: Props) { }: Props) {
const [query, setQuery] = useState(''); const [query, setQuery] = useState('');
return ( return (
<Combobox <Combobox
by="id"
disabled={disabled} disabled={disabled}
value={selectedOption} // eslint-disable-next-line @typescript-eslint/ban-ts-comment
onChange={onSelectOption}> // @ts-ignore
multiple={false}
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
nullable={nullable}
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
value={value}
onChange={(newValue) => {
if (newValue == null) {
return;
}
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
onSelect(newValue as TypeaheadOption);
}}>
<Combobox.Label <Combobox.Label
className={clsx( className={clsx(
isLabelHidden isLabelHidden
@ -54,10 +75,11 @@ export default function Typeahead({
disabled && 'pointer-events-none select-none bg-slate-100', disabled && 'pointer-events-none select-none bg-slate-100',
)} )}
displayValue={(option) => displayValue={(option) =>
(option as unknown as TypeaheadOption).label (option as unknown as TypeaheadOption)?.label
} }
onChange={(event) => { onChange={(event) => {
!disabled && onQueryChange(event.target.value, event); setQuery(event.target.value);
onQueryChange(event.target.value, event);
}} }}
/> />
<Combobox.Button className="absolute inset-y-0 right-0 flex items-center pr-2"> <Combobox.Button className="absolute inset-y-0 right-0 flex items-center pr-2">
@ -76,7 +98,7 @@ export default function Typeahead({
<Combobox.Options className="absolute mt-1 max-h-60 w-full overflow-auto rounded-md bg-white py-1 text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none sm:text-sm"> <Combobox.Options className="absolute mt-1 max-h-60 w-full overflow-auto rounded-md bg-white py-1 text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none sm:text-sm">
{options.length === 0 && query !== '' ? ( {options.length === 0 && query !== '' ? (
<div className="relative cursor-default select-none py-2 px-4 text-slate-700"> <div className="relative cursor-default select-none py-2 px-4 text-slate-700">
Nothing found. {noResultsMessage}
</div> </div>
) : ( ) : (
options.map((option) => ( options.map((option) => (

Loading…
Cancel
Save