From 7bb6c4602d4f7ad23607209439e7377bafa960de Mon Sep 17 00:00:00 2001 From: Nevo David Date: Fri, 3 Jan 2025 13:57:09 +0700 Subject: [PATCH] feat: customer filter --- .../components/launches/add.edit.model.tsx | 46 ++--- .../components/launches/calendar.context.tsx | 11 +- .../src/components/launches/filters.tsx | 177 ++++++++++-------- .../components/launches/select.customer.tsx | 44 +++++ .../database/prisma/posts/posts.repository.ts | 5 + .../src/database/prisma/schema.prisma | 1 + .../src/dtos/posts/get.posts.dto.ts | 10 +- 7 files changed, 184 insertions(+), 110 deletions(-) create mode 100644 apps/frontend/src/components/launches/select.customer.tsx diff --git a/apps/frontend/src/components/launches/add.edit.model.tsx b/apps/frontend/src/components/launches/add.edit.model.tsx index 229be6e8..e0f7db99 100644 --- a/apps/frontend/src/components/launches/add.edit.model.tsx +++ b/apps/frontend/src/components/launches/add.edit.model.tsx @@ -1,7 +1,17 @@ 'use client'; import React, { - ClipboardEventHandler, FC, Fragment, MouseEventHandler, useCallback, useEffect, useMemo, useRef, ClipboardEvent, useState, memo + ClipboardEventHandler, + FC, + Fragment, + MouseEventHandler, + useCallback, + useEffect, + useMemo, + useRef, + ClipboardEvent, + useState, + memo, } from 'react'; import dayjs from 'dayjs'; import { Integrations } from '@gitroom/frontend/components/launches/calendar.context'; @@ -50,6 +60,7 @@ import { useClickOutside } from '@gitroom/frontend/components/layout/click.outsi import { useUppyUploader } from '@gitroom/frontend/components/media/new.uploader'; import { LoadingComponent } from '@gitroom/frontend/components/layout/loading'; import { DropFiles } from '@gitroom/frontend/components/layout/drop.files'; +import { SelectCustomer } from '@gitroom/frontend/components/launches/select.customer'; function countCharacters(text: string, type: string): number { if (type !== 'x') { @@ -94,10 +105,6 @@ export const AddEditModal: FC<{ return list; }, [customer, ints]); - const totalCustomers = useMemo(() => { - return uniqBy(ints, (i) => i?.customer?.id).length; - }, [ints]); - const [dateState, setDateState] = useState(date); // hook to open a new modal @@ -519,28 +526,13 @@ export const AddEditModal: FC<{ information={data} onChange={setPostFor} /> - {totalCustomers > 1 && ( - - )} + { + setCustomer(val); + setSelectedIntegrations([]); + }} + /> {!selectedIntegrations.length && ( , - integrations: [] as (Integrations & {refreshNeeded?: boolean})[], + integrations: [] as (Integrations & { refreshNeeded?: boolean })[], trendings: [] as string[], posts: [] as Array, reloadCalendarView: () => { @@ -42,6 +43,7 @@ export const CalendarContext = createContext({ currentDay: 0 | 1 | 2 | 3 | 4 | 5 | 6; currentMonth: number; display: 'week' | 'month' | 'day'; + customer: string | null; }) => { /** empty **/ }, @@ -66,7 +68,7 @@ export interface Integrations { customer?: { name?: string; id?: string; - } + }; } function getWeekNumber(date: Date) { @@ -94,6 +96,7 @@ export const CalendarWeekProvider: FC<{ const fetch = useFetch(); const [internalData, setInternalData] = useState([] as any[]); const [trendings] = useState([]); + const searchParams = useSearchParams(); const display = searchParams.get('display') || 'week'; @@ -110,6 +113,7 @@ export const CalendarWeekProvider: FC<{ currentWeek: +(searchParams.get('week') || getWeekNumber(new Date())), currentMonth: +(searchParams.get('month') || dayjs().month()), currentYear: +(searchParams.get('year') || dayjs().year()), + customer: (searchParams.get('customer') as string) || null, display, }); @@ -120,6 +124,7 @@ export const CalendarWeekProvider: FC<{ week: filters.currentWeek.toString(), month: (filters.currentMonth + 1).toString(), year: filters.currentYear.toString(), + customer: filters?.customer?.toString() || '', }).toString(); }, [filters, display]); @@ -142,6 +147,7 @@ export const CalendarWeekProvider: FC<{ currentYear: number; currentMonth: number; display: 'week' | 'month' | 'day'; + customer: string | null; }) => { setFilters(filters); setInternalData([]); @@ -152,6 +158,7 @@ export const CalendarWeekProvider: FC<{ `month=${filters.currentMonth}`, `year=${filters.currentYear}`, `display=${filters.display}`, + filters.customer ? `customer=${filters.customer}` : ``, ].filter((f) => f); window.history.replaceState(null, '', `/launches?${path.join('&')}`); }, diff --git a/apps/frontend/src/components/launches/filters.tsx b/apps/frontend/src/components/launches/filters.tsx index db21fa5b..4f883728 100644 --- a/apps/frontend/src/components/launches/filters.tsx +++ b/apps/frontend/src/components/launches/filters.tsx @@ -4,6 +4,7 @@ import clsx from 'clsx'; import dayjs from 'dayjs'; import { useCallback } from 'react'; import { isUSCitizen } from './helpers/isuscitizen.utils'; +import { SelectCustomer } from '@gitroom/frontend/components/launches/select.customer'; export const Filters = () => { const week = useCalendar(); @@ -13,30 +14,30 @@ export const Filters = () => { .year(week.currentYear) .isoWeek(week.currentWeek) .day(week.currentDay) - .format(isUSCitizen() ? 'MM/DD/YYYY' :'DD/MM/YYYY') + .format(isUSCitizen() ? 'MM/DD/YYYY' : 'DD/MM/YYYY') : week.display === 'week' ? dayjs() .year(week.currentYear) .isoWeek(week.currentWeek) .startOf('isoWeek') - .format(isUSCitizen() ? 'MM/DD/YYYY' :'DD/MM/YYYY') + + .format(isUSCitizen() ? 'MM/DD/YYYY' : 'DD/MM/YYYY') + ' - ' + dayjs() .year(week.currentYear) .isoWeek(week.currentWeek) .endOf('isoWeek') - .format(isUSCitizen() ? 'MM/DD/YYYY' :'DD/MM/YYYY') + .format(isUSCitizen() ? 'MM/DD/YYYY' : 'DD/MM/YYYY') : dayjs() .year(week.currentYear) .month(week.currentMonth) .startOf('month') - .format(isUSCitizen() ? 'MM/DD/YYYY' :'DD/MM/YYYY') + + .format(isUSCitizen() ? 'MM/DD/YYYY' : 'DD/MM/YYYY') + ' - ' + dayjs() .year(week.currentYear) .month(week.currentMonth) .endOf('month') - .format(isUSCitizen() ? 'MM/DD/YYYY' :'DD/MM/YYYY'); + .format(isUSCitizen() ? 'MM/DD/YYYY' : 'DD/MM/YYYY'); const setDay = useCallback(() => { week.setFilters({ @@ -45,6 +46,7 @@ export const Filters = () => { currentYear: dayjs().year(), currentMonth: dayjs().month(), display: 'day', + customer: week.customer, }); }, [week]); @@ -55,6 +57,7 @@ export const Filters = () => { currentYear: dayjs().year(), currentMonth: dayjs().month(), display: 'week', + customer: week.customer, }); }, [week]); @@ -65,9 +68,24 @@ export const Filters = () => { currentWeek: dayjs().isoWeek(), currentYear: dayjs().year(), display: 'month', + customer: week.customer, }); }, [week]); + const setCustomer = useCallback( + (customer: string) => { + week.setFilters({ + currentDay: week.currentDay, + currentMonth: week.currentMonth, + currentWeek: week.currentWeek, + currentYear: week.currentYear, + display: week.display as any, + customer: customer, + }); + }, + [week] + ); + const next = useCallback(() => { const increaseDay = week.display === 'day'; const increaseWeek = @@ -77,6 +95,7 @@ export const Filters = () => { week.display === 'month' || (increaseWeek && week.currentWeek === 52); week.setFilters({ + customer: week.customer, currentDay: (!increaseDay ? 0 : week.currentDay === 6 @@ -116,6 +135,7 @@ export const Filters = () => { week.display === 'month' || (decreaseWeek && week.currentWeek === 1); week.setFilters({ + customer: week.customer, currentDay: (!decreaseDay ? 0 : week.currentDay === 0 @@ -147,77 +167,82 @@ export const Filters = () => { ]); return (
-
-
- - - -
-
- {week.display === 'day' - ? `${dayjs() - .month(week.currentMonth) - .week(week.currentWeek) - .day(week.currentDay) - .format('dddd')}` - : week.display === 'week' - ? `Week ${week.currentWeek}` - : `${dayjs().month(week.currentMonth).format('MMMM')}`} -
-
- - - -
-
{betweenDates}
-
-
-
- Day -
-
- Week -
-
- Month -
-
+
+
+ + + +
+
+ {week.display === 'day' + ? `${dayjs() + .month(week.currentMonth) + .week(week.currentWeek) + .day(week.currentDay) + .format('dddd')}` + : week.display === 'week' + ? `Week ${week.currentWeek}` + : `${dayjs().month(week.currentMonth).format('MMMM')}`} +
+
+ + + +
+
{betweenDates}
+
+ setCustomer(customer)} + integrations={week.integrations} + /> +
+
+ Day +
+
+ Week +
+
+ Month +
+
); }; diff --git a/apps/frontend/src/components/launches/select.customer.tsx b/apps/frontend/src/components/launches/select.customer.tsx new file mode 100644 index 00000000..7fd4e5ea --- /dev/null +++ b/apps/frontend/src/components/launches/select.customer.tsx @@ -0,0 +1,44 @@ +import { Select } from '@gitroom/react/form/select'; +import { uniqBy } from 'lodash'; +import React, { FC, useMemo, useState } from 'react'; +import { Integrations } from '@gitroom/frontend/components/launches/calendar.context'; + +export const SelectCustomer: FC<{ + onChange: (value: string) => void; + integrations: Integrations[]; + customer?: string; +}> = (props) => { + const { onChange, integrations, customer: currentCustomer } = props; + const [customer, setCustomer] = useState(currentCustomer || ''); + + const totalCustomers = useMemo(() => { + return uniqBy(integrations, (i) => i?.customer?.id).length; + }, [integrations]); + + if (totalCustomers <= 1) { + return null; + } + + return ( + + ); +}; diff --git a/libraries/nestjs-libraries/src/database/prisma/posts/posts.repository.ts b/libraries/nestjs-libraries/src/database/prisma/posts/posts.repository.ts index 732d2724..9f4c8523 100644 --- a/libraries/nestjs-libraries/src/database/prisma/posts/posts.repository.ts +++ b/libraries/nestjs-libraries/src/database/prisma/posts/posts.repository.ts @@ -110,6 +110,11 @@ export class PostsRepository { }, deletedAt: null, parentPostId: null, + ...query.customer ? { + integration: { + customerId: query.customer, + } + }: {}, }, select: { id: true, diff --git a/libraries/nestjs-libraries/src/database/prisma/schema.prisma b/libraries/nestjs-libraries/src/database/prisma/schema.prisma index 97dec774..6d609616 100644 --- a/libraries/nestjs-libraries/src/database/prisma/schema.prisma +++ b/libraries/nestjs-libraries/src/database/prisma/schema.prisma @@ -293,6 +293,7 @@ model Integration { @@index([rootInternalId]) @@index([updatedAt]) @@index([deletedAt]) + @@index([customerId]) @@unique([organizationId, internalId]) } diff --git a/libraries/nestjs-libraries/src/dtos/posts/get.posts.dto.ts b/libraries/nestjs-libraries/src/dtos/posts/get.posts.dto.ts index b228b821..b1b45273 100644 --- a/libraries/nestjs-libraries/src/dtos/posts/get.posts.dto.ts +++ b/libraries/nestjs-libraries/src/dtos/posts/get.posts.dto.ts @@ -1,10 +1,6 @@ import { Type } from 'class-transformer'; import { - IsDefined, - IsIn, - IsNumber, - Max, - Min, + IsDefined, IsIn, IsNumber, IsOptional, IsString, Max, Min } from 'class-validator'; import dayjs from 'dayjs'; @@ -36,4 +32,8 @@ export class GetPostsDto { @Max(dayjs().add(10, 'year').year()) @Min(2022) year: number; + + @IsOptional() + @IsString() + customer: string; }