Persist 'projectId' between page reloads
This commit is contained in:
parent
510abf3031
commit
e1fb2875ae
8 changed files with 59 additions and 63 deletions
|
@ -1,2 +1,3 @@
|
|||
import { createContext } from "react";
|
||||
export const ParamContext = createContext<Record<string, string>>({});
|
||||
export const CookieContext = createContext<Record<string, string>>({});
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { PropsWithChildren } from "react";
|
||||
import { ApolloClient, ApolloProvider, InMemoryCache } from "@apollo/client";
|
||||
import { ParamContext } from "~/Context";
|
||||
import { ParamContext, CookieContext } from "~/Context";
|
||||
|
||||
export function App({ children }: PropsWithChildren<{}>) {
|
||||
const client = new ApolloClient({
|
||||
|
@ -13,9 +13,14 @@ export function App({ children }: PropsWithChildren<{}>) {
|
|||
.split(",")
|
||||
.map(e => e.split("=")),
|
||||
);
|
||||
const cookies = Object.fromEntries(
|
||||
document.cookie.split(";").map(e => e.split("=")),
|
||||
);
|
||||
return (
|
||||
<ParamContext.Provider value={params}>
|
||||
<ApolloProvider client={client}>{children}</ApolloProvider>
|
||||
</ParamContext.Provider>
|
||||
<CookieContext.Provider value={cookies}>
|
||||
<ParamContext.Provider value={params}>
|
||||
<ApolloProvider client={client}>{children}</ApolloProvider>
|
||||
</ParamContext.Provider>
|
||||
</CookieContext.Provider>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { useContext } from "react";
|
||||
import { ParamContext } from "~/Context";
|
||||
import { ParamContext, CookieContext } from "~/Context";
|
||||
import { Maybe } from "~/types/schema";
|
||||
import { ProjectSelect } from "~/components/ProjectSelect";
|
||||
const BASE_CLASSNAMES = ["block", "w-3/4", "no-underline", "p-3", "mt-2"];
|
||||
|
@ -32,6 +32,7 @@ const find = (path: string, bar: Bar): Maybe<Item> => {
|
|||
|
||||
export function NavBar() {
|
||||
const params = useContext(ParamContext);
|
||||
const cookies = useContext(CookieContext);
|
||||
const bar: Bar = {
|
||||
Tasks: [
|
||||
{ text: "All tasks", href: "/tasks/" },
|
||||
|
@ -69,13 +70,15 @@ export function NavBar() {
|
|||
</>
|
||||
);
|
||||
})}
|
||||
<h3>Filters</h3>
|
||||
<h3>Scope</h3>
|
||||
<ProjectSelect
|
||||
selected={params.projectId}
|
||||
selected={params.projectId || cookies.projectId}
|
||||
onChange={project => {
|
||||
if (project) {
|
||||
document.cookie = `projectId=${project.id}; path=/`;
|
||||
location.hash = `projectId=${project.id}`;
|
||||
} else {
|
||||
document.cookie = `projectId=; path=/; max-age=0`;
|
||||
location.hash = "";
|
||||
}
|
||||
location.reload();
|
||||
|
|
|
@ -17,7 +17,7 @@ const ACTIVE_CLASSNAME = [
|
|||
"border-solid",
|
||||
"border-secondary",
|
||||
"text-primary",
|
||||
"bg-secondary"
|
||||
"bg-secondary",
|
||||
].join(" ");
|
||||
|
||||
type Props = {
|
||||
|
@ -36,7 +36,7 @@ export const Select = ({ onChange, options, selected, placeholder }: Props) => {
|
|||
const selectOptions = [{ label: placeholder, value: "" }, ...options];
|
||||
const [isOpen, setIsOpen] = useState<boolean>(false);
|
||||
const [option, setOption] = useState<Option>(
|
||||
options.find(o => o.value === selected) || selectOptions[0]
|
||||
options.find(o => o.value === selected) || selectOptions[0],
|
||||
);
|
||||
const onClick = (option: Option) => {
|
||||
if (isOpen) {
|
||||
|
@ -49,21 +49,21 @@ export const Select = ({ onChange, options, selected, placeholder }: Props) => {
|
|||
};
|
||||
const getClassName = (o1: Option, o2: Option) => {
|
||||
if (o1.value === o2.value) {
|
||||
return ACTIVE_CLASSNAME
|
||||
return ACTIVE_CLASSNAME;
|
||||
} else {
|
||||
return (!isOpen && o1.value !== o2.value) ? "hidden" : LI_CLASSNAME;
|
||||
return !isOpen && o1.value !== o2.value ? "hidden" : LI_CLASSNAME;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const onDocumentClick = () => {
|
||||
if (isOpen) {
|
||||
setIsOpen(false)
|
||||
setIsOpen(false);
|
||||
}
|
||||
}
|
||||
document.body.addEventListener('click', onDocumentClick)
|
||||
return () => document.body.removeEventListener('click', onDocumentClick)
|
||||
}, [isOpen])
|
||||
};
|
||||
document.body.addEventListener("click", onDocumentClick);
|
||||
return () => document.body.removeEventListener("click", onDocumentClick);
|
||||
}, [isOpen]);
|
||||
|
||||
return (
|
||||
<ul>
|
||||
|
@ -71,7 +71,7 @@ export const Select = ({ onChange, options, selected, placeholder }: Props) => {
|
|||
<li
|
||||
key={i}
|
||||
data-value={o.value}
|
||||
onClick={(e) => [e.stopPropagation(), onClick(o)]}
|
||||
onClick={e => [e.stopPropagation(), onClick(o)]}
|
||||
className={getClassName(o, option)}
|
||||
>
|
||||
{o.label}
|
||||
|
|
|
@ -33,7 +33,7 @@ export function Tabs({ defaultLabel, labels, onChange }: Props) {
|
|||
<li className={classnames(classNames)}>
|
||||
<a
|
||||
href={location.hash}
|
||||
onClick={(e) => [e.preventDefault(), setActive(tab)]}
|
||||
onClick={e => [e.preventDefault(), setActive(tab)]}
|
||||
className="block p-2 text-smaller no-underline"
|
||||
>
|
||||
{tab.label}
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
import { useEffect, useState, useContext } from "react";
|
||||
import { ParamContext } from "~/Context";
|
||||
import { ParamContext, CookieContext } from "~/Context";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { useCreateTask } from "~/hooks/mutations/useCreateTask";
|
||||
import { useUpdateTask } from "~/hooks/mutations/useUpdateTask";
|
||||
import { useFindTask } from "~/hooks/queries/useFindTask";
|
||||
import { useProjects } from "~/hooks/queries/useProjects";
|
||||
import { Task, Project, TaskInput, Maybe } from "~/types/schema";
|
||||
import { Task, TaskInput, Maybe } from "~/types/schema";
|
||||
import { rendermd } from "~/lib/markdown-utils";
|
||||
import { NavBar } from "~/components/NavBar";
|
||||
import { Tabs, Tab } from "~/components/Tabs";
|
||||
|
@ -25,33 +24,32 @@ const DEFAULT_TASK_CONTENT = [
|
|||
|
||||
export function Task() {
|
||||
const params = useContext(ParamContext);
|
||||
const taskId = params.id ? parseInt(params.id) : null;
|
||||
const cookies = useContext(CookieContext);
|
||||
const projectId: Maybe<number> = Number(
|
||||
params.projectId || cookies.projectId,
|
||||
);
|
||||
const taskId = params.id ? Number(params.id) : null;
|
||||
const {
|
||||
register,
|
||||
handleSubmit,
|
||||
watch,
|
||||
setValue: set,
|
||||
} = useForm<TaskInput>({
|
||||
defaultValues: { projectId: 1 },
|
||||
});
|
||||
} = useForm<TaskInput>({});
|
||||
const [isEditable, setIsEditable] = useState<boolean>(!taskId);
|
||||
const createTask = useCreateTask();
|
||||
const updateTask = useUpdateTask();
|
||||
const [createTask, updateTask] = [useCreateTask(), useUpdateTask()];
|
||||
const { data: taskData, loading: findingTask } = useFindTask(Number(taskId));
|
||||
const { data: projectsData, loading: findingProjects } = useProjects();
|
||||
const task = taskData?.findTask;
|
||||
const projects = projectsData?.projects;
|
||||
const content = watch("content");
|
||||
const projectId: Maybe<number> = watch("projectId");
|
||||
const onSave = (input: TaskInput) => {
|
||||
const onSave = async (input: TaskInput) => {
|
||||
if (taskId) {
|
||||
updateTask({ variables: { taskId, input } }).then(
|
||||
() => (location.href = "/tasks"),
|
||||
);
|
||||
} else {
|
||||
createTask({ variables: { input } }).then(
|
||||
() => (location.href = "/tasks"),
|
||||
);
|
||||
const res = await createTask({ variables: { input } });
|
||||
const payload = res?.data?.createTask;
|
||||
const { errors } = payload;
|
||||
errors.length ? alert(errors) : (location.href = "/tasks");
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -61,10 +59,12 @@ export function Task() {
|
|||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
set("projectId", task?.project?.id);
|
||||
if (task?.project?.id) {
|
||||
set("projectId", task?.project?.id);
|
||||
}
|
||||
}, [task?.project?.id]);
|
||||
|
||||
if (findingProjects || findingTask) {
|
||||
if (findingTask) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -76,23 +76,12 @@ export function Task() {
|
|||
<div className="w-3/4">
|
||||
<h1>{task ? "Edit task" : "New task"}</h1>
|
||||
<form onSubmit={handleSubmit(onSave)}>
|
||||
<select
|
||||
{...register("projectId")}
|
||||
className="p-3 w-3/4 mb-3"
|
||||
value={projectId}
|
||||
onChange={event => {
|
||||
const v: string = event.target.value;
|
||||
set("projectId", Number(v));
|
||||
}}
|
||||
>
|
||||
{projects.map((project: Project, key: number) => {
|
||||
return (
|
||||
<option key={key} value={project.id}>
|
||||
{project.name}
|
||||
</option>
|
||||
);
|
||||
<input
|
||||
type="hidden"
|
||||
{...register("projectId", {
|
||||
value: projectId,
|
||||
})}
|
||||
</select>
|
||||
/>
|
||||
<input
|
||||
className="p-3 flex w-3/4 mb-3"
|
||||
type="text"
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { useEffect, useContext } from "react";
|
||||
import { ParamContext } from "~/Context";
|
||||
import { ParamContext, CookieContext } from "~/Context";
|
||||
import { NavBar } from "~/components/NavBar";
|
||||
import { Group } from "~/components/Group";
|
||||
import { TaskStatus, Maybe } from "~/types/schema";
|
||||
|
@ -7,9 +7,11 @@ import { useTasks } from "~/hooks/queries/useTasks";
|
|||
|
||||
export function Tasks() {
|
||||
const params = useContext(ParamContext);
|
||||
const projectId: Maybe<number> = params.projectId
|
||||
? parseInt(params.projectId)
|
||||
: null;
|
||||
const cookies = useContext(CookieContext);
|
||||
const projectId: Maybe<number> =
|
||||
params.projectId || cookies.projectId
|
||||
? Number(params.projectId || cookies.projectId)
|
||||
: null;
|
||||
|
||||
useEffect(() => {
|
||||
document.title = "Tasks";
|
||||
|
|
|
@ -1,8 +1,4 @@
|
|||
import {
|
||||
CreateTaskPayload,
|
||||
MutationCreateTaskArgs,
|
||||
TaskInput,
|
||||
} from "~/types/schema";
|
||||
import { TaskInput } from "~/types/schema";
|
||||
import { gql, useMutation } from "@apollo/client";
|
||||
|
||||
const GQL = gql`
|
||||
|
@ -18,7 +14,7 @@ type TArgs = {
|
|||
};
|
||||
|
||||
export function useCreateTask() {
|
||||
const [create] = useMutation<CreateTaskPayload, MutationCreateTaskArgs>(GQL);
|
||||
const [create] = useMutation(GQL);
|
||||
return ({ variables: { input }, ...rest }: TArgs) => {
|
||||
const projectId = Number(input.projectId);
|
||||
const variables = { input: { ...input, ...{ projectId } } };
|
||||
|
|
Loading…
Reference in a new issue