Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add: sections feature #86

Merged
merged 4 commits into from
Jul 2, 2022
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
1 change: 1 addition & 0 deletions pages/create.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ const Create: NextPageWithLayout = () => {
members: [],
dateAdded: serverTimestamp(),
requests: [],
sections: [],
isPublic: false,
};

Expand Down
137 changes: 130 additions & 7 deletions pages/room.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,17 @@ import React, { useState, useMemo, useCallback } from 'react';
import { nanoid } from 'nanoid';
import Tippy from '@tippyjs/react';
import toast from 'react-hot-toast';
import { useRouter } from 'next/router';
import { AiOutlinePlus } from 'react-icons/ai';
import { addDoc, collection, serverTimestamp } from 'firebase/firestore';
import { MdOutlineCategory } from 'react-icons/md';
import {
doc,
addDoc,
arrayUnion,
updateDoc,
collection,
serverTimestamp,
} from 'firebase/firestore';
import { BsCheckCircleFill, BsInfoCircleFill, BsFilter } from 'react-icons/bs';

import { db } from '@/config/firebase';
Expand All @@ -16,6 +25,7 @@ import { useNextQuery, useTaskFields, useUpload } from '@/hooks';
import { RoomProvider, useRoomContext } from '@/contexts/RoomContext';
import {
Info,
InfoSection,
InviteUser,
RoomMenu,
Requests,
Expand All @@ -27,6 +37,7 @@ import {
// eslint-disable-next-line import/no-extraneous-dependencies
import 'tippy.js/dist/tippy.css';
import { isNearDeadline, isPastDeadline } from '@/utils/functions';
import { BiPieChart } from 'react-icons/bi';

type SortDate =
| 'date_added_asc'
Expand All @@ -42,14 +53,17 @@ const Room: NextPageWithLayout = () => {

const [addTaskModal, setAddTaskModal] = useState(false);
const [filterModal, setFilterModal] = useState(false);
const [addSectionModal, setAddSectionModal] = useState(false);
const [loading, setLoading] = useState(false);

const [sortBy, setSortBy] = useState<SortDate>('date_added_desc');
const [filterBy, setFilterBy] = useState<FilterStatus>('All');
const [sectionName, setSectionName] = useState<string>('');

const { push } = useRouter();
const { data } = useAuth();
const { userTag } = data;
const { room, tasks, tasksLoading } = useRoomContext();
const { room, tasks, tasksLoading, roomSection } = useRoomContext();

const sortedTasks = useMemo(() => {
return tasks
Expand All @@ -75,8 +89,13 @@ const Room: NextPageWithLayout = () => {
}

return 0;
})
?.filter((task) => {
if (roomSection === task.section) return task;
if (!roomSection) return task.section === '';
return false;
});
}, [tasks, sortBy, filterBy]);
}, [tasks, sortBy, filterBy, roomSection]);

// eslint-disable-next-line prefer-destructuring
const tab = useNextQuery('tab');
Expand Down Expand Up @@ -106,6 +125,7 @@ const Room: NextPageWithLayout = () => {
dateAdded: serverTimestamp(),
dueDate,
url,
section: roomSection ?? '',
};

if (images.length > 0) {
Expand All @@ -128,6 +148,38 @@ const Room: NextPageWithLayout = () => {
}
};

const addSection = async () => {
try {
if (!sectionName) toast.error('Section Name is required.');
else {
setAddSectionModal(false);
setLoading(true);

const roomRef = doc(db, `rooms/${room.id}`);
if (room.sections?.length >= 4) {
toast.error('Max sections reached (4)');
setLoading(false);
} else if (sectionName) {
await updateDoc(roomRef, {
sections: arrayUnion(sectionName),
});
toast.success('Section Added');
push({
pathname: '/room',
query: { id: room.id, section: sectionName },
});
setTimeout(() => {
setLoading(false);
setSectionName('');
}, 300);
}
}
} catch (e: any) {
toast.error(e.message);
setLoading(false);
}
};

const SortDate = ({ label, value }: { label: string; value: SortDate }) =>
useMemo(() => {
const id = nanoid();
Expand Down Expand Up @@ -191,14 +243,29 @@ const Room: NextPageWithLayout = () => {
if (tab === 'invite') return <InviteUser />;
if (tab === 'requests') return <Requests />;

let loaderMessage;
if (uploading) loaderMessage = 'Uploading Image(s)';
else if (sectionName) loaderMessage = 'Adding Section';
else loaderMessage = 'Adding Task';

return (
<>
{loading && (
<TaskLoader msg={uploading ? 'Uploading Image(s)' : 'Adding Task'} />
)}
{loading && <TaskLoader msg={loaderMessage} />}
<div className='my-4 flex items-center justify-between font-medium'>
<div className='flex items-center space-x-2'>
<h2 className='text-sm'>{room.name}</h2>
<button
type='button'
onClick={() => push({ pathname: '/room', query: { id: room.id } })}
className='flex cursor-pointer'
>
<h2 className='text-sm'>{room.name}</h2>
</button>
{roomSection && (
<>
<div className='h-[15px] w-[1px] bg-gray-400' />
<h2 className='text-sm'>{roomSection.replaceAll('+', ' ')}</h2>
</>
)}
<Tippy
placement='bottom'
content={
Expand All @@ -223,6 +290,12 @@ const Room: NextPageWithLayout = () => {
</Tippy>
</div>
<div className='flex space-x-2'>
<Button
type='button'
Icon={MdOutlineCategory}
onClick={() => setAddSectionModal(true)}
className='room-btn'
/>
<Button
type='button'
Icon={AiOutlinePlus}
Expand Down Expand Up @@ -290,6 +363,56 @@ const Room: NextPageWithLayout = () => {
}
/>

<Modal
isOpen={addSectionModal}
setIsOpen={setAddSectionModal}
title='Sections'
proceed={{
action: addSection,
text: 'Add Section',
style: 'bg-blue-500',
}}
body={
<div>
{room.sections?.length > 0 && (
<>
<p className='my-2 text-sm text-gray-600'>
{room.sections.length} out of 4 sections
</p>

{room.sections.map((name) => (
<InfoSection
key={name}
title={name}
label='Section'
onClick={() => {
push({
pathname: '/room',
query: { id: room.id, section: name },
});
setAddSectionModal(false);
}}
Icon={BiPieChart}
/>
))}
<hr className='my-4' />
</>
)}

<p className='text-sm text-gray-600'>Add New Section</p>
<div className='room-input-container my-4'>
<input
onChange={(e) => setSectionName(e.target.value)}
value={sectionName}
type='text'
placeholder='Section Name'
className='room-input'
/>
</div>
</div>
}
/>

{sortedTasks && sortedTasks?.length > 0 ? (
<section className='mb-8 space-y-2'>
{sortedTasks
Expand Down
4 changes: 4 additions & 0 deletions src/components/Room/TaskFields.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ interface TaskFieldsProps extends Omit<ModalProps, 'body' | 'title'> {
setUrl: SetState;
images: File[];
setImages: SetState<File[]>;
section: string;
setSection: SetState;
}

const TaskFields = ({
Expand All @@ -31,6 +33,8 @@ const TaskFields = ({
setUrl,
images,
setImages,
section,
setSection,
...rest
}: TaskFieldsProps) => {
const [imgUrls, setImgUrls] = useState(['']);
Expand Down
6 changes: 4 additions & 2 deletions src/contexts/RoomContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ interface RoomContextValues {
isAdmin: boolean;
roomLoading: boolean;
tasksLoading: boolean;
roomSection: string;
}

const RoomContext = createContext({} as RoomContextValues);
Expand All @@ -25,6 +26,7 @@ const RoomProvider: React.FC = ({ children }) => {
data: { userTag },
} = useAuth();
const roomId = useNextQuery('id');
const roomSection = useNextQuery('section') ?? '';
const [room, roomLoading] = useRoom(roomId);

const [tasks, tasksLoading] = useCol<ITask>(
Expand All @@ -37,8 +39,8 @@ const RoomProvider: React.FC = ({ children }) => {
);

const contextValues = useMemo(
() => ({ room, tasks, isAdmin, roomLoading, tasksLoading }),
[room, tasks, roomLoading, tasksLoading]
() => ({ room, tasks, isAdmin, roomLoading, tasksLoading, roomSection }),
[room, tasks, roomLoading, tasksLoading, roomSection]
);

return (
Expand Down
4 changes: 4 additions & 0 deletions src/hooks/useTaskFields.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,27 @@ const useTaskFields = (task?: ITask) => {
);
const [images, setImages] = useState<File[]>([]);
const [url, setUrl] = useState(task?.url ?? '');
const [section, setSection] = useState(task?.section ?? '');

const props = {
description,
dueDate,
images,
url,
section,
setDesc,
setDueDate,
setImages,
setUrl,
setSection,
};

const reset = () => {
setDesc(task?.description ?? '');
setDueDate(task?.dueDate ? task.dueDate.toDate() : null);
setImages([]);
setUrl(task?.url ?? '');
setSection(task?.section ?? '');
};

return { props, reset };
Expand Down
2 changes: 2 additions & 0 deletions types/models.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ interface IRoom {
members: string[];
dateAdded: any;
requests: string[];
sections: string[];
isPublic: boolean;
}

Expand All @@ -34,4 +35,5 @@ interface ITask {
dueDate?: any;
completedBy: string[];
imgUrls?: string[];
section?: string;
}