Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,17 @@ import { ColumnData } from '@/types/dashboardView';
import Button from '@/components/Button';
import Image from 'next/image';
import Card from './Card';
import useModalStore from '@/store/modalStore';
import CreateTaskModal from './CreateTaskModal';
import styles from './Column.module.css';

function Column({ color, title, totalCount, id, items }: ColumnData) {
const { openModal } = useModalStore();

const handleCreateTask = () => {
openModal(<CreateTaskModal />);
};

return (
<div className={styles.column}>
<div className={styles.header}>
Expand All @@ -32,6 +40,7 @@ function Column({ color, title, totalCount, id, items }: ColumnData) {
type="button"
className={styles.createCard}
aria-label="์ปฌ๋Ÿผ ์ƒ์„ฑ ๋ฒ„ํŠผ"
onClick={handleCreateTask}
>
<Image
src="/icons/add.svg"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
.modal {
display: flex;
flex-direction: column;
gap: 24px;
padding: 28px 20px 20px;
}

.modal h2 {
font-size: 20px;
font-weight: 700;
line-height: 24px;
color: var(--black-100);
}

.footer {
display: flex;
align-items: center;
gap: 11px;
font-size: 14px;
font-weight: 600;
line-height: 24px;
}

.footer button {
padding: 9px 0;
}

.cancel.cancel {
border: 1px solid var(--gray-300);
background-color: var(--white);
color: var(--gray-500);
}

@media screen and (min-width: 768px) {
.modal {
gap: 32px;
padding: 32px;
}

.footer {
font-size: 16px;
line-height: 26px;
gap: 8px;
}

.footer button {
padding: 14px 0;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import useModalStore from '@/store/modalStore';
import { useForm } from 'react-hook-form';
import Button from '@/components/Button';
import Input from '@/components/Input';
import styles from './CreateTaskModal.module.css';
import SearchDropdown from './SearchDropdown';
import DatePicker from './DatePicker';

export interface ManagerOption {
id: number;
name: string;
}

export default function CreateTaskModal() {
const { closeModal } = useModalStore();
const {
register,
handleSubmit,
formState: { errors, isValid },
} = useForm<{ email: string }>({ mode: 'onChange' });

const onSubmit = () => {
// closeModal();
};

const options = [
{ id: 1, name: '๊น€๊น€๊น€' },
{ id: 2, name: '์ด์ด์ด' },
{ id: 3, name: '์ตœ์ตœ์ตœ' },
];

const handleSelect = (selected: ManagerOption) => {
console.log('์„ ํƒ๋œ ๋‹ด๋‹น์ž:', selected);
};

return (
<form className={styles.modal} onSubmit={handleSubmit(onSubmit)}>
<h2>ํ• ์ผ ์ƒ์„ฑ</h2>
<SearchDropdown options={options} onSelect={handleSelect} />
<Input name="title" label="์ œ๋ชฉ" placeholder="์ œ๋ชฉ์„ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”" />
<DatePicker />
<div className={styles.footer}>
<Button onClick={closeModal} className={styles.cancel}>
์ทจ์†Œ
</Button>
<Button type="submit" disabled={!isValid}>
์ƒ์„ฑ
</Button>
</div>
</form>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
.container {
display: flex;
flex-direction: column;
gap: 8px;
position: relative;
width: 287px;
}

.label {
font-size: 14px;
font-weight: 500;
line-height: 24px;
color: var(--black-100);
}

.inputWrapper {
position: relative;
}

.input {
width: 100%;
border: 1px solid var(--gray-300);
border-radius: 6px;
font-size: 14px;
font-weight: 400;
line-height: 24px;
padding: 11px 14px;
cursor: pointer;
}

.input:focus {
outline-color: var(--violet);
}

.input::placeholder {
color: var(--gray-400);
}

.calendarWrapper {
position: absolute;
top: 50px;
left: 0;
width: 100%;
background: var(--white);
border: 1px solid var(--gray-200);
border-radius: 4px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
z-index: 1000;
}

.calendarHeader {
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px;
background: var(--gray-200);
border-bottom: 1px solid var(--gray-200);
}

.daysOfWeek {
display: grid;
grid-template-columns: repeat(7, 1fr);
background: var(--gray-300);
text-align: center;
padding: 5px 0;
}

.day {
font-weight: bold;
color: var(--black-100);
}

.days {
display: grid;
grid-template-columns: repeat(7, 1fr);
text-align: center;
}

.dayButton {
padding: 10px;
}

.dayButton:hover {
background: var(--violet-light);
}

.dayButton.selected {
background: var(--violet);
color: var(--white);
border-radius: 50%;
}

.dayButton:disabled {
cursor: default;
}

.timePicker {
padding: 10px;
border-top: 1px solid var(--gray-200);
}

.timeSelect {
width: 100%;
padding: 8px;
border: 1px solid var(--gray-200);
border-radius: 4px;
}

.image {
display: block;
}

@media screen and (min-width: 768px) {
.container {
width: 520px;
}

.label {
font-size: 18px;
line-height: 26px;
}
}
139 changes: 139 additions & 0 deletions src/app/(with-header-sidebar)/dashboard/[id]/components/DatePicker.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
'use client';

import React, { useState, ChangeEvent } from 'react';
import Image from 'next/image';
import styles from './DatePicker.module.css';

const DatePicker = () => {
const [selectedDate, setSelectedDate] = useState<string>('');
const [selectedTime, setSelectedTime] = useState<string>('');
const [isCalendarVisible, setIsCalendarVisible] = useState(false);
const [currentMonth, setCurrentMonth] = useState(new Date());

const daysOfWeek = ['์ผ', '์›”', 'ํ™”', '์ˆ˜', '๋ชฉ', '๊ธˆ', 'ํ† '];

const getDaysInMonth = (date: Date) => {
const startOfMonth = new Date(date.getFullYear(), date.getMonth(), 1);
const endOfMonth = new Date(date.getFullYear(), date.getMonth() + 1, 0);
const days: (Date | null)[] = [];

for (let i = 0; i < startOfMonth.getDay(); i++) {
days.push(null);
}

for (let i = 1; i <= endOfMonth.getDate(); i++) {
days.push(new Date(date.getFullYear(), date.getMonth(), i));
}

return days;
};

const handleDateClick = (date: Date) => {
setSelectedDate(date.toISOString().split('T')[0]);
};

const handleMonthChange = (direction: 'prev' | 'next') => {
setCurrentMonth(
(prev) =>
new Date(
prev.getFullYear(),
prev.getMonth() + (direction === 'next' ? 1 : -1),
1
)
);
};

const handleTimeChange = (event: ChangeEvent<HTMLSelectElement>) => {
setSelectedTime(event.target.value);
};

const daysInMonth = getDaysInMonth(currentMonth);

return (
<div className={styles.container}>
<label className={styles.label}>๋งˆ๊ฐ์ผ</label>
<div className={styles.inputWrapper}>
<input
type="text"
className={styles.input}
value={`${selectedDate} ${selectedTime}`}
placeholder="๋‚ ์งœ๋ฅผ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”"
readOnly
onClick={() => setIsCalendarVisible((prev) => !prev)}
/>
{isCalendarVisible && (
<div className={styles.calendarWrapper}>
<div className={styles.calendar}>
<div className={styles.calendarHeader}>
<button onClick={() => handleMonthChange('prev')}>
<Image
src="/icons/arrow_left.svg"
width={16}
height={16}
alt="์™ผ์ชฝ"
className={styles.image}
/>
</button>
<span>
{currentMonth.getFullYear()}๋…„ {currentMonth.getMonth() + 1}์›”
</span>
<button onClick={() => handleMonthChange('next')}>
<Image
src="/icons/arrow_right.svg"
width={16}
height={16}
alt="์˜ค๋ฅธ์ชฝ"
className={styles.image}
/>
</button>
</div>
<div className={styles.daysOfWeek}>
{daysOfWeek.map((day) => (
<div key={day} className={styles.day}>
{day}
</div>
))}
</div>
<div className={styles.days}>
{daysInMonth.map((date, index) => (
<button
key={index}
className={`${styles.dayButton} ${
date &&
selectedDate === date.toISOString().split('T')[0] &&
styles.selected
}`}
onClick={() => date && handleDateClick(date)}
disabled={!date}
>
{date?.getDate() || ''}
</button>
))}
</div>
</div>
<div className={styles.timePicker}>
<select
className={styles.timeSelect}
value={selectedTime}
onChange={handleTimeChange}
>
{Array.from({ length: 24 }, (_, hour) =>
['00', '30'].map((minute) => {
const time = `${hour.toString().padStart(2, '0')}:${minute}`;
return (
<option key={time} value={time}>
{time}
</option>
);
})
)}
</select>
</div>
</div>
)}
</div>
</div>
);
};

export default DatePicker;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

์š”๊ฑฐ ์‹œ๊ฐ„๋„ ๊ฐ™์ด ์žˆ์œผ๋‹ˆ๊น DateTimePicker๊ฐ€ ๋” ์ข‹์•„๋ณด์—ฌ์š”!

Loading