Skip to content

Commit

Permalink
Merge pull request #245 from ruby10127130/feat/manage/projects-chore
Browse files Browse the repository at this point in the history
Feat/manage/projects chore
  • Loading branch information
JohnsonMao authored Jan 24, 2025
2 parents 28fedb3 + 1209144 commit 4883ce6
Show file tree
Hide file tree
Showing 6 changed files with 106 additions and 112 deletions.
97 changes: 52 additions & 45 deletions components/Projects/Form/Select.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { useState } from 'react';
import { cn } from '@/utils/cn';
import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown';

/**
* @typedef {Object} Option
Expand All @@ -20,73 +21,79 @@ interface SelectProps {
}

const Select = ({ options, className, isDisabled = false }: SelectProps) => {
const [isOpen, setIsOpen] = useState(false);
const [selectedLabel, setSelectedLabel] = useState("全部計畫");
const handleSelect = (label: string) => {
setSelectedLabel(label);
setIsOpen(false);
};

if (!options.length) {
console.error('no option');
return false;
}
const [isOpen, setIsOpen] = useState(false);
const [selectedValue, setSelectedValue] = useState("全部計畫");
const handleSelect = (value: string, label: string) => {
setSelectedValue(label);
setIsOpen(false);
console.log("Selected:", value);
};

return (
<div className={cn(`relative inline-block w-full`, className)}>
<div className={cn(
"relative inline-block w-full",
className
)}
>
<button
disabled={isDisabled}
type="button"
onClick={() => setIsOpen(!isOpen)}
className={`
block w-full px-4 py-2 text-left
rounded-lg shadow-sm
focus:outline-none focus:ring-0 focus:ring-primary-base
${isDisabled ?
className={cn(
'block w-full px-4 py-2 text-left',
'rounded-lg shadow-sm flex flex-row items-center justify-between',
'focus:outline-none focus:ring-0 focus:ring-primary-base',
isDisabled ?
'text-white bg-primary-lighter'
:
'text-basic-400 bg-primary-lightest '}
`}
'text-basic-400 bg-primary-lightest'
)}
>
{selectedValue}
<span className="absolute right-4 top-1/2 transform -translate-y-1/2">
<svg
className={`
w-5 h-5 transition-transform ease-linear
${isOpen ? 'rotate-180' : ''}
${isDisabled ? 'text-white' : 'text-gray-400'}
`}
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M19 9l-7 7-7-7"
/>
</svg>
{selectedLabel}
<span>
<KeyboardArrowDownIcon
className={cn(
"w-5 h-5",
"transform transition-transform ease-linear",
isOpen ? "rotate-180" : "rotate-0",
isDisabled ? "text-white" : "text-gray-400",
)}
/>
</span>

</button>
<ul className={`absolute left-0 mt-2 w-full bg-white border border-gray-300 rounded-md shadow-lg overflow-hidden transition-all duration-300 ease-in transform p-[10px]
${isOpen ?
<ul className={cn(
"absolute left-0",
"w-full mt-2 p-[10px] overflow-hidden bg-white",
"border border-gray-300",
"transition-all duration-300 ease-in transform",
"rounded-md shadow-lg",
isOpen ?
"max-h-64 opacity-100 scale-y-100"
:
"max-h-0 opacity-0 scale-y-95"}`
}
"max-h-0 opacity-0 scale-y-95"
)}
>
{options.map((option) => (
<li
key={option.value}
onClick={() => handleSelect(option.value, option.label)}
className="px-4 py-2 rounded-[5px]
text-basic-400
hover:bg-primary-lightest
cursor-pointer"
className="px-0"
>
{option.label}
<button
type="button"
className={cn(
"w-full px-2 py-2 rounded-[5px] text-basic-400 text-left",
"hover:bg-primary-lightest hover:cursor-pointer",
"focus:bg-primary-lightest focus:ring-0 focus:outline-none"
)}
onClick={() => handleSelect(option.label)}
>
{option.label}
</button>
</li>
))}
</ul>
Expand Down
16 changes: 10 additions & 6 deletions components/Projects/GoBackButton.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import KeyboardArrowLeftIcon from '@mui/icons-material/KeyboardArrowLeft';
import { cn } from '@/utils/cn';
import React from 'react';

/**
* Button component for rendering customizable buttons.
Expand All @@ -15,13 +15,16 @@ import { cn } from '@/utils/cn';
* - The text displayed on the button.
* @param {string} [className]
* - Optional custom classes to apply to the button (uses Tailwind CSS).
* @param {React.ReactNode} [icon]
* - Optional custom icon to apply on the button. Typically, a MUI Icon component.
* @param {() => void} onClick
* - Function to call when the button is clicked.
* @returns {JSX.Element} The button element.
*/

interface ButtonProps {
id: string,
icon?: React.ReactNode,
buttonText: string;
className?: string;
onClick: () => void;
Expand All @@ -32,6 +35,7 @@ const GoBackButton = ({
className = "",
id = "",
onClick = () => { },
icon = null,
}: ButtonProps
) => {
return (
Expand All @@ -40,12 +44,12 @@ const GoBackButton = ({
name={id}
type="button"
onClick={onClick}
className={cn(`flex flex-row items-center group`, className)}
className={cn(
"flex flex-row items-center group",
className
)}
>
<KeyboardArrowLeftIcon className="
text-basic-400
group-hover:text-primary-base"
/>
{icon && icon}
<span className="
text-basic-400 font-sans text-sm font-normal
group-hover:text-primary-base"
Expand Down
11 changes: 1 addition & 10 deletions layout/ProjectLayout.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,12 @@
import { useState, useEffect } from 'react';
import { usePathname, useSearchParams } from 'next/navigation';

import Sidebar from '@/shared/components/Sidebar';
import DefaultLayout from './DefaultLayout';

export default function ProjectLayout({ children }: React.PropsWithChildren) {
const [projectId, setProjectId] = useState(null);
const pathname = usePathname();
const searchParams = useSearchParams();
const id = searchParams.get('id');
useEffect(() => {
if (id) {
setProjectId(projectId);
} else {
setProjectId(null);
}
}, [id]);
const projectId = searchParams.get('id');
return (
<DefaultLayout>
<div className="bg-primary-palest">
Expand Down
60 changes: 26 additions & 34 deletions pages/manage/projects/index.jsx
Original file line number Diff line number Diff line change
@@ -1,27 +1,28 @@
import { useState, useEffect } from 'react';
import { useRouter } from 'next/router';
import { useSelector } from 'react-redux';
import KeyboardArrowLeftIcon from '@mui/icons-material/KeyboardArrowLeft';
import Select from '@/components/Projects/Form/Select';
import AccessDenied from '@/components/Projects/AccessDenied';
import AccessDenied from '@/shared/components/AccessDenied';
import GoBackButton
from '@/components/Projects/GoBackButton';
import { ProtectedComponent } from '@/contexts/Auth';
import emptyCoverImg from '@/public/assets/empty-cover.png';
import CircleIcon from '@mui/icons-material/Circle';
import MoreVertIcon from '@mui/icons-material/MoreVert';
import More from '@/components/Projects/More';
import Button from '@/shared/components/Button';
import { cn } from '@/utils/cn';

const Projects = () => {
const maxProjects = 3;
const router = useRouter();
const [isEditPermitted, setIsEditPermitted] = useState(false);
const [isAddedPermitted, setIsAddedPermitted] = useState(true);
const userState = useSelector((state) => state.user);
const projects = Array.isArray(userState.marathons) ? userState.marathons : [];
const isEditPermitted = Boolean(projects.length);
const isAddedDenied = projects.length >= maxProjects;
const options = [
{ value: "all", label: "全部計畫" },
{ value: "learning-marathon", label: "學習馬拉松" },
];
const [projects, setProjects] = useState([]);
const getProjectType = (eventId) => {
switch (eventId) {
case "2025S1":
Expand All @@ -31,21 +32,6 @@ const Projects = () => {
}
};

useEffect(() => {
if (userState._id) {
if (userState.marathons.length) {
setProjects(userState.marathons);
setIsEditPermitted(true);
}
}
}, [userState]);

useEffect(() => {
if (projects.length > (maxProjects - 1)) {
setIsAddedPermitted(false);
}
}, [projects]);

return (
<ProtectedComponent>
<div className="bg-[#F3FCFC] md:py-8">
Expand All @@ -59,11 +45,22 @@ const Projects = () => {
id: 'island'
}
})}
icon={
(
<KeyboardArrowLeftIcon
className="
text-basic-400
group-hover:text-primary-base"
/>
)}
buttonText="返回 我的小島"
/>
<h2 className="heading-md text-basic-400">學習計畫</h2>
<div
className="w-full flex flex-col justify-between bg-white rounded-xl py-3 px-6"
className={cn(
"w-full flex flex-col justify-between bg-white rounded-xl",
"py-3 px-3 md:px-6")
}
style={{
boxShadow: '0px 4px 10px 0px rgba(196, 194, 193, 0.40)'
}}
Expand All @@ -74,16 +71,16 @@ const Projects = () => {
options={options}
className="max-w-[200px]"
/>
<button
type="button"
className="flex-shrink-0 py-[5px] px-5 rounded-[20px] bg-primary-lighter text-white font-sans text-base font-normal hover:bg-primary-lighter"
disabled
<Button
isDisabled={isAddedDenied}
variant="solid"
className="hover:cursor-pointer flex-shrink-0"
>
新增計畫
</button>
</Button>
</div>
{
!isAddedPermitted && (
isAddedDenied && (
<p className="font-sans font-normal text-[#FF9526]">
島上空間有限,計畫滿三個就不能再增加了{`><`}
</p>
Expand Down Expand Up @@ -135,12 +132,7 @@ const Projects = () => {
<CircleIcon className="text-primary-base max-w-2 max-h-2" />
<span className="font-sans text-xs font-bold leading-[140%]">{project.isPublic ? '公開' : '不公開'}</span>
</p>
{/* <div className=""> */}
<More
// className=""
projectId={project._id}
/>
{/* </div> */}
<More projectId={project._id} />
</div>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useRouter } from 'next/router';
import AccessDeniedImg from '@/public/assets/projects/access-denied.png';
import Button from "./Form/Button";
import Button from "@/shared/components/Button";

const AccessDenied = () => {
const router = useRouter();
Expand All @@ -12,11 +12,11 @@ const AccessDenied = () => {
rounded-2xl"
>
<p className="
font-sans
font-sans
text-basic-400
text-sm
font-bold
leading-[140%]
text-sm
font-bold
leading-[140%]
"
>
這裡目前只開放給
Expand All @@ -33,19 +33,19 @@ const AccessDenied = () => {
<div className="w-full flex flex-col gap-3 justify-center md:flex-row">
<Button
id="editMyCardButton"
onClick={() => router.push('/')}
buttonText="編輯個人名片"
buttonStyle="outline"
/>
onClick={() => router.push('/manage/mycard')}
variant="outline"
color="primary"
>
編輯個人名片
</Button>
<Button
id="findResourcesButton"
onClick={() => router.push('/')}
buttonText="尋找學習資源"
buttonStyle="default"
style={{
boxShadow: '0px 4px 10px 0px rgba(89, 182, 178, 0.5)'
}}
/>
variant="solid"
onClick={() => router.push('/resources')}
>
查看學習資源
</Button>
</div>
</div>
);
Expand Down
2 changes: 1 addition & 1 deletion shared/components/Button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ function Button({
variant === ButtonVariantEnum.Outline && [
'rounded-full border border-solid transition-colors',
color === ButtonColorEnum.Primary &&
'border-primary-base text-basic-400 hover:bg-primary hover:text-basic-white',
'border-primary-base text-primary-base hover:bg-primary-base hover:text-basic-white',
color === ButtonColorEnum.White &&
'border-basic-white text-basic-white hover:bg-basic-white hover:text-primary-base',
],
Expand Down

0 comments on commit 4883ce6

Please sign in to comment.