Skip to content

Commit

Permalink
Merge pull request #180 from sohosai/feat/committee-edit-project
Browse files Browse the repository at this point in the history
feat: 企画編集ページ
  • Loading branch information
appare45 authored Apr 14, 2024
2 parents 3446bc6 + c2afc88 commit f0ed310
Show file tree
Hide file tree
Showing 11 changed files with 342 additions and 28 deletions.
19 changes: 3 additions & 16 deletions src/app/committee/projects/ProjectsList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,11 @@ import { ProjectCategoryFormatter } from "@/components/ProjectCategoryFormatter"
import { ProjectAttributesBadge } from "@/components/project/AttirbutesBadge";
import { components } from "@/schema";
import { css, cx } from "@styled-system/css";
import { grid, hstack, vstack } from "@styled-system/patterns";
import MailAddressIcon from "@/components/assets/MailAddress.svg";
import { grid, vstack } from "@styled-system/patterns";
import React from "react";
import Link from "next/link";
import Image from "next/image";
import toast from "react-hot-toast";
import { projectCategoryItemStyle } from "@/components/formFields/styles";
import { UserWithAddress } from "../../../components/project/UserWithAddress";

const ProjectRow: React.FC<{ data: components["schemas"]["ProjectSummary"] }> = ({ data }) => {
return (
Expand All @@ -22,24 +20,13 @@ const ProjectRow: React.FC<{ data: components["schemas"]["ProjectSummary"] }> =
<div className={css({ fontWeight: "bold", color: "gray.500", fontSize: "lg" })}>
{("000" + data.index).slice(-3)}
</div>
{/* 将来的に企画責任者名を含める? */}
<div className={vstack({ alignItems: "start" })}>
<Link
href={`/committee/projects/${data.id}`}
className={css({ fontWeight: "bold", fontSize: "lg", display: "block" })}>
{data.title}
</Link>
<div
className={hstack({ alignItems: "center", zIndex: 2, cursor: "pointer" })}
onClick={() => {
navigator.clipboard
.writeText(data.owner_email)
.then(() => toast.success("企画責任者のメールアドレスをコピーしました"))
.catch(() => toast.error("コピーに失敗しました"));
}}>
<Image src={MailAddressIcon} alt="" className={css({ height: "full" })} />
{data.owner_name}
</div>
<UserWithAddress name={data.owner_name} email={data.owner_email} />
</div>
<div className={vstack({ alignItems: "end" })}>
<div
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
"use client";
import { ProjectCategoryFormatter } from "@/components/ProjectCategoryFormatter";
import { projectCategories } from "@/lib/valibot";
import { css, cx } from "@styled-system/css";
import { hstack, visuallyHidden } from "@styled-system/patterns";
import { UseFormRegisterReturn } from "react-hook-form";

export const ProjectCategoryEditor: React.FC<{ register: UseFormRegisterReturn }> = ({ register }) => {
return (
<div className={hstack({ flexWrap: "wrap" })}>
{projectCategories.map((category) => (
<label
key={category}
className={cx(
css({
paddingBlock: 2,
paddingInline: 6,
borderRadius: "2xl",
cursor: "pointer",
color: "gray.600",
backgroundColor: "gray.200",
fontSize: "sm",
fontWeight: "bold",
boxSizing: "border-box",
"&:has(> input:checked)": {
color: "sohosai.purple",
outline: "2px solid ",
backgroundColor: "white",
},
}),
)}>
<ProjectCategoryFormatter category={category} />
<input type="radio" value={category} className={visuallyHidden()} {...register} />
</label>
))}
</div>
);
};
181 changes: 181 additions & 0 deletions src/app/committee/projects/[project_id]/edit/ProjectEditForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
"use client";
import { Button } from "@/components/Button";
import { basicErrorMessageStyle, basicFormStyle } from "@/components/formFields/styles";
import { AttributesFormatter } from "@/components/project/AttributesFormatter";
import { client } from "@/lib/openapi";
import { UpdateProjectCommitteeSchema, UpdateProjectCommitteeSchemaType, projectAttributes } from "@/lib/valibot";
import { valibotResolver } from "@hookform/resolvers/valibot";
import { css } from "@styled-system/css";
import { hstack, stack, visuallyHidden } from "@styled-system/patterns";
import { useForm } from "react-hook-form";
import Arrow from "./three_arrow_left.svg";
import Image from "next/image";
import { getNewInvitationId, shareURL } from "@/components/project/ProjectView";
import { components } from "@/schema";
import toast from "react-hot-toast";
import { useRouter } from "next/navigation";
import { ProjectCategoryEditor } from "./ProjectCategoryEditor";
export const runtime = "edge";

export const ProjectEditForm: React.FC<{ project: components["schemas"]["Project"] }> = ({ project }) => {
const router = useRouter();
const {
register,
formState: { errors },
handleSubmit,
} = useForm<UpdateProjectCommitteeSchemaType>({
resolver: valibotResolver(UpdateProjectCommitteeSchema),
defaultValues: project,
mode: "onBlur",
});

const lableAndInputStyle = css({ fontWeight: "bold", "& > input": { fontWeight: "normal", marginTop: 2 } });
const updateProject = async (data: UpdateProjectCommitteeSchemaType) => {
if (!project) return;
client
.PUT("/projects/{project_id}", {
params: {
path: {
project_id: project.id,
},
},
body: data as components["schemas"]["UpdateProject"],
})
.then((res) => {
if (res.error) {
toast.error("変更を保存できませんでした");
return;
}
toast.success("変更を保存しました");
router.push(`/committee/projects/${project.id}`);
})
.catch(() => toast.error("変更を保存できませんでした"));
};

return (
<form className={stack({ gap: 4 })} onSubmit={handleSubmit(updateProject)}>
<div className={hstack({ flexDirection: "row-reverse" })}>
<Button color="primary" type="submit">
保存
</Button>
</div>
<label className={lableAndInputStyle}>
企画名
<input
className={basicFormStyle()}
placeholder="20文字以内で入力"
{...register("title", { value: project?.title })}
/>
<p className={css({ color: "gray.400", fontWeight: "normal" })}>
※絵文字不可。半角全角英数字・半角記号は3字で仮名2文字にカウントします。
</p>
{errors.title && <span className={basicErrorMessageStyle}>{errors.title.message}</span>}
</label>
<label className={lableAndInputStyle}>
企画名(ふりがな)
<input
className={basicFormStyle()}
placeholder="20文字以内で入力"
{...register("kana_title", { value: project?.kana_title })}
/>
{errors.kana_title && <span className={basicErrorMessageStyle}>{errors.kana_title.message}</span>}
</label>
<label className={lableAndInputStyle}>
企画団体名
<input className={basicFormStyle()} {...register("group_name", { value: project?.group_name })} />
<p className={css({ color: "gray.400", fontWeight: "normal" })}>
※絵文字不可。半角全角英数字・半角記号は3字で仮名2文字にカウントします。
</p>
{errors.group_name && <span className={basicErrorMessageStyle}>{errors.group_name.message}</span>}
</label>
<label className={lableAndInputStyle}>
企画団体名(ふりがな)
<input
className={basicFormStyle()}
{...register("kana_group_name", {
value: project?.kana_group_name,
})}
/>
{errors.kana_group_name && <span className={basicErrorMessageStyle}>{errors.kana_group_name.message}</span>}
</label>
<section className={hstack({ justifyContent: "space-between", marginTop: 10 })}>
<span className={css({ fontWeight: "bold", fontSize: "lg" })}>企画責任者</span>
<div className={hstack()}>
<span>{project?.owner_name}</span>
<Image src={Arrow} alt="" />
<Button
type="button"
color="secondary"
onClick={async () =>
shareURL(`${window.location.origin}/invitation/${await getNewInvitationId(project.id, "owner")}`)
}>
変更用URLを発行
</Button>
</div>
</section>
<section className={hstack({ justifyContent: "space-between" })}>
<span className={css({ fontWeight: "bold", fontSize: "lg" })}>副企画責任者</span>
<div className={hstack()}>
<span>{project?.sub_owner_name ?? "未設定"}</span>
<Image src={Arrow} alt="" />
<Button
type="button"
color="secondary"
onClick={async () => shareURL(await getNewInvitationId(project.id, "sub_owner"))}>
変更用URLを発行
</Button>
</div>
</section>
<fieldset className={css({ marginTop: 10 })}>
<div className={hstack({ marginBottom: 4 })}>
<legend>企画区分</legend>
<legend className={css({ fontSize: "sm", color: "gray.500", fontWeight: "bold" })}>
どれか一つを選択してください
</legend>
</div>
<ProjectCategoryEditor register={register("category", { value: project?.category })} />
</fieldset>
{errors.category && <span className={basicErrorMessageStyle}>{errors.category.message}</span>}
<fieldset className={css({ marginTop: 5 })}>
<div className={hstack({ marginBottom: 4 })}>
<legend>企画属性</legend>
<legend className={css({ fontSize: "sm", color: "gray.500", fontWeight: "bold" })}>
当てはまるものをすべて選択してください
</legend>
</div>
<div className={hstack()}>
{projectAttributes.map((attribute) => (
<label
key={attribute}
className={css({
paddingBlock: 2,
paddingInline: 6,
borderRadius: "2xl",
cursor: "pointer",
color: "gray.600",
fontSize: "sm",
outline: "3px solid token(colors.gray.300)",
fontWeight: "bold",
boxSizing: "border-box",
"&:has(> input:checked)": {
color: "sohosai.purple",
outline: "2px solid ",
backgroundColor: "white",
},
})}>
<AttributesFormatter attribute={attribute} />
<input
type="checkbox"
value={attribute}
defaultChecked={project.attributes.includes(attribute as components["schemas"]["ProjectAttribute"])}
{...register("attributes")}
className={visuallyHidden()}
/>
</label>
))}
</div>
</fieldset>
{errors.attributes && <span className={basicErrorMessageStyle}>{errors.attributes.message}</span>}
</form>
);
};
38 changes: 38 additions & 0 deletions src/app/committee/projects/[project_id]/edit/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
"use client";

import { assignType } from "@/lib/openapi";
import { css } from "@styled-system/css";
import { container } from "@styled-system/patterns";
import { NextPage } from "next";
import useSWR from "swr";
import Link from "next/link";
import { ProjectEditForm } from "./ProjectEditForm";

export const runtime = "edge";

const ProjectEditPage: NextPage<{ params: { project_id: string } }> = ({ params }) => {
const { data: rawProject, isLoading, error } = useSWR(`/projects/${params.project_id}`);
const project = rawProject ? assignType("/projects/{project_id}", rawProject) : undefined;
if (isLoading) {
return;
}

if (error) {
return <>{error}</>;
}

if (!project) {
return "企画の読み込みに失敗しました";
}

return (
<main className={container({ maxWidth: "4xl", marginY: 8 })}>
<Link className={css({ color: "sohosai.purple", fontSize: "xs" })} href={`/committee/projects/${project.id}`}>
←企画に戻る
</Link>
<ProjectEditForm project={project} />
</main>
);
};

export default ProjectEditPage;
22 changes: 22 additions & 0 deletions src/app/committee/projects/[project_id]/edit/three_arrow_left.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 6 additions & 1 deletion src/app/committee/projects/[project_id]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { css } from "@styled-system/css";
import { assignType, client } from "@/lib/openapi";
import Link from "next/link";
import useSWR from "swr";
import { ProjectTableView } from "@/app/dashboard/ProjectView";
import { ProjectTableView } from "@/components/project/ProjectView";
import { Button } from "@/components/Button";
import toast from "react-hot-toast";
import { useRouter } from "next/navigation";
Expand Down Expand Up @@ -60,6 +60,11 @@ const NewsDetailsPage = ({ params }: { params: { project_id: string } }) => {
企画詳細
</h2>

<div className={hstack({ flexDir: "row-reverse" })}>
<Button color="blue" onClick={() => router.push(`/committee/projects/${project.id}/edit`)}>
編集
</Button>
</div>
<ProjectTableView projectData={project} isCommittee />

<section className={hstack({ justifyContent: "space-between" })}>
Expand Down
2 changes: 1 addition & 1 deletion src/app/dashboard/Project.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { Title } from "@/components/Title";
import { css } from "@styled-system/css";
import { useState } from "react";
import { Button } from "@/components/Button";
import { ProjectTableView } from "./ProjectView";
import { ProjectTableView } from "../../components/project/ProjectView";
import { assignType } from "@/lib/openapi";
import useSWR from "swr";
import { basicErrorMessageStyle } from "@/components/formFields/styles";
Expand Down
4 changes: 1 addition & 3 deletions src/components/project/AttributesFormatter.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
import { components } from "@/schema";

export const AttributesFormatter = ({ attribute }: { attribute: components["schemas"]["ProjectAttribute"] }) => {
export const AttributesFormatter = ({ attribute }: { attribute: string }) => {
switch (attribute) {
case "academic":
return "学術認定企画";
Expand Down
Loading

0 comments on commit f0ed310

Please sign in to comment.