diff --git a/package-lock.json b/package-lock.json index d6f130c..aa2bb4f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,6 +15,7 @@ "@tanstack/react-query": "^5.59.19", "babel-plugin-react-compiler": "19.0.0-beta-63b359f-20241101", "deck.gl": "^9.0.34", + "es-toolkit": "^1.27.0", "jwt-decode": "^4.0.0", "leaflet": "^1.9.4", "lucide-react": "^0.454.0", @@ -4721,6 +4722,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/es-toolkit": { + "version": "1.27.0", + "resolved": "https://registry.npmjs.org/es-toolkit/-/es-toolkit-1.27.0.tgz", + "integrity": "sha512-ETSFA+ZJArcuSCpzD2TjAy6UHpx4E4uqFsoDg9F/nTLogrLmVVZQ+zNxco5h7cWnA1nNak07IXsLcaSMih+ZPQ==" + }, "node_modules/escalade": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", diff --git a/src/app/casos-activos/ofertas/page.tsx b/src/app/casos-activos/ofertas/page.tsx index 27966b5..a697fcb 100644 --- a/src/app/casos-activos/ofertas/page.tsx +++ b/src/app/casos-activos/ofertas/page.tsx @@ -33,12 +33,12 @@ function Ofertas() { const [currentCount, setCurrentCount] = useState(0); const [search, setSearch] = useState(''); const debouncedSearch = useDebouncedValue(search, 500); - + const itemsPerPage = 10; const numPages = (count: number) => { return Math.ceil(count / itemsPerPage) || 0; }; - + const updateFilter = useCallback( (filter: 'ayuda' | 'pueblo' | 'search' | 'page', value: string | number) => { const params = new URLSearchParams(searchParams.toString()); @@ -51,13 +51,13 @@ function Ofertas() { }, [router, searchParams], ); - + const [filtroData, setFiltroData] = useState({ ayuda: searchParams.get('acepta') || 'todas', pueblo: searchParams.get('pueblo') || 'todos', search: searchParams.get('search') || '', }); - + const changeDataFilter = useCallback( (type: 'ayuda' | 'pueblo' | 'search', newFilter: string) => { setFiltroData((prev) => ({ @@ -72,10 +72,9 @@ function Ofertas() { useEffect(() => { if (debouncedSearch !== filtroData.search) { changeDataFilter('search', debouncedSearch); - } + } }, [debouncedSearch, changeDataFilter, filtroData.search]); - function changePage(newPage: number) { setCurrentPage(newPage); updateFilter('page', newPage); diff --git a/src/app/casos-activos/solicitudes/index.tsx b/src/app/casos-activos/solicitudes/index.tsx index 5f35894..b965ee1 100644 --- a/src/app/casos-activos/solicitudes/index.tsx +++ b/src/app/casos-activos/solicitudes/index.tsx @@ -8,7 +8,7 @@ import { useTowns } from '@/context/TownProvider'; import { TabNavigationCount } from '@/components/TabNavigation'; import Modal from '@/components/Modal'; import { MAP_MODAL_NAME } from '@/components/map/map'; -import { HelpRequestData } from '@/types/Requests'; +import { HelpRequestData, HelpRequestWAssignments } from '@/types/Requests'; import SolicitudCard from '@/components/solicitudes/SolicitudCard'; import SolicitudList, { isStringTrue } from '@/components/solicitudes/SolicitudList'; import SolicitudMap from '@/components/solicitudes/SolicitudMap'; @@ -28,14 +28,14 @@ function getDataFiltered(data: HelpRequestData[], filters: DataFilter[]) { type SolicitudesProps = { count: TabNavigationCount; - data: HelpRequestData[]; + data: HelpRequestWAssignments[]; }; export function Solicitudes({ data, count }: SolicitudesProps) { const { towns } = useTowns(); const searchParams = useSearchParams(); const router = useRouter(); - const [selectedMarker, setSelectedMarker] = useState(null); + const [selectedMarker, setSelectedMarker] = useState(null); const [dataFiltered, setDataFiltered] = useState(data); @@ -104,7 +104,7 @@ export function Solicitudes({ data, count }: SolicitudesProps) {
{ // Remove unused properties to reduce the payload size const { coordinates, location, ...rest } = d; @@ -17,7 +17,7 @@ function parseData(data: Database['public']['Tables']['help_requests']['Row'][]) // Fix the coordinates to 3 decimals so locations have a 100m precision latitude: Number(d.latitude?.toFixed(3)), longitude: Number(d.longitude?.toFixed(3)), - } as HelpRequestData; + } as HelpRequestWAssignments; }); } diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 49d7513..adbd967 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -7,6 +7,7 @@ import { Toaster } from 'sonner'; import { PropsWithChildren } from 'react'; import { QueryClientProvider } from '@/context/QueryClientProvider'; import { RoleProvider } from '@/context/RoleProvider'; +import { TownProvider } from '../context/TownProvider'; export const metadata = { title: 'Ajuda Dana - Sistema de Coordinación', @@ -21,9 +22,11 @@ export default async function RootLayout({ children }: PropsWithChildren) { - - {children} - + + + {children} + + diff --git a/src/app/solicitudes/[id]/page.tsx b/src/app/solicitudes/[id]/page.tsx index e7ab8f7..4e0350b 100644 --- a/src/app/solicitudes/[id]/page.tsx +++ b/src/app/solicitudes/[id]/page.tsx @@ -5,12 +5,12 @@ import SolicitudCard from '@/components/solicitudes/SolicitudCard'; import { useParams } from 'next/navigation'; import SolicitudComments from '@/components/Comments/SolicitudComments'; import { useEffect, useState } from 'react'; -import { SelectedHelpData } from '../../../types/Requests'; -import { getOne } from '@/lib/actions'; +import { SelectedHelpDataWAssignment } from '../../../types/Requests'; +import { getOneWithAssignments } from '@/lib/actions'; export default function CasoDetalle() { const [loading, setLoading] = useState(true); - const [data, setData] = useState(null); + const [data, setData] = useState(null); const { id } = useParams<{ id: string }>(); useEffect(() => { @@ -18,8 +18,8 @@ export default function CasoDetalle() { try { setLoading(true); - const requestResponse = await getOne(Number(id)); - setData(requestResponse as SelectedHelpData); + const requestResponse = await getOneWithAssignments(Number(id)); + setData(requestResponse as SelectedHelpDataWAssignment); setLoading(false); } catch (err) { diff --git a/src/app/solicitudes/page.tsx b/src/app/solicitudes/page.tsx index 1db4296..45b197c 100644 --- a/src/app/solicitudes/page.tsx +++ b/src/app/solicitudes/page.tsx @@ -5,8 +5,8 @@ import SolicitudCard from '@/components/solicitudes/SolicitudCard'; import { useSession } from '@/context/SessionProvider'; import Link from 'next/link'; import { useQuery } from '@tanstack/react-query'; -import { SelectedHelpData } from '@/types/Requests'; -import { getSolicitudesByUser } from '@/lib/actions'; +import { SelectedHelpDataWAssignment } from '@/types/Requests'; +import { getSolicitudesWAssignemntsByUser } from '@/lib/actions'; export const dynamic = 'force-dynamic'; @@ -18,9 +18,9 @@ export default function ListaSolicitudes() { data: requests, isLoading, error, - } = useQuery({ + } = useQuery({ queryKey: ['help_requests', { user_id: userId, type: 'necesita' }], - queryFn: () => getSolicitudesByUser(userId || ''), + queryFn: () => getSolicitudesWAssignemntsByUser(userId || ''), }); if (isLoading) { diff --git a/src/components/AsignarSolicitudButton.tsx b/src/components/AsignarSolicitudButton.tsx index 12103eb..1455367 100644 --- a/src/components/AsignarSolicitudButton.tsx +++ b/src/components/AsignarSolicitudButton.tsx @@ -1,8 +1,8 @@ 'use client'; import { useSession } from '@/context/SessionProvider'; -import { HelpRequestAssignmentData, SelectedHelpData } from '@/types/Requests'; -import { getAssignments, assign, unassign } from '@/lib/actions'; +import { SelectedHelpData, SelectedHelpDataWAssignment } from '@/types/Requests'; +import { assign, unassign, getSolicitudesWAssignemntsByUser } from '@/lib/actions'; import { MouseEvent } from 'react'; import { Spinner } from '@/components/Spinner'; import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; @@ -23,12 +23,12 @@ export default function AsignarSolicitudButton({ helpRequest }: AsignarSolicitud const router = useRouter(); const { - data: assignments, + data: solicitudesUser, isLoading, error, - } = useQuery({ - queryKey: ['help_request_assignments', { id: helpRequest.id }], - queryFn: () => getAssignments(helpRequest.id), + } = useQuery({ + queryKey: ['help_requests', { user_id: userId, type: 'necesita' }], + queryFn: () => getSolicitudesWAssignemntsByUser(userId || ''), }); const queryClient = useQueryClient(); @@ -84,9 +84,9 @@ export default function AsignarSolicitudButton({ helpRequest }: AsignarSolicitud } if (isLoading) return ; - if (error || assignments === undefined) return <>; + if (error || solicitudesUser === undefined) return <>; - const userAssignment = assignments.find((x) => x.user_id === session.user?.id); + const userAssignment = solicitudesUser.find((x) => x.id === helpRequest.id); const userIsAssigned = !!userAssignment; if (!session || !session.user) diff --git a/src/components/OfferCard.tsx b/src/components/OfferCard.tsx index 762e68e..7d9b241 100644 --- a/src/components/OfferCard.tsx +++ b/src/components/OfferCard.tsx @@ -46,7 +46,7 @@ export default function OfferCard({ caso, showLink = true, showEdit = false, hig

- {caso.description && getHighlightedText(caso.description, highlightedText)} + {caso.description && getHighlightedText(caso.description, highlightedText)}

diff --git a/src/components/PhoneInfo.tsx b/src/components/PhoneInfo.tsx index a0338ed..141556d 100644 --- a/src/components/PhoneInfo.tsx +++ b/src/components/PhoneInfo.tsx @@ -1,27 +1,29 @@ import { useQuery } from '@tanstack/react-query'; import { useSession } from '@/context/SessionProvider'; -import { getAssignments } from '@/lib/actions'; -import { SelectedHelpData } from '@/types/Requests'; +import { getSolicitudesWAssignemntsByUser } from '@/lib/actions'; +import { SelectedHelpDataWAssignment } from '@/types/Requests'; type PhoneInfoProps = { - caseInfo: SelectedHelpData; + caseInfo: SelectedHelpDataWAssignment; isAdmin: boolean; }; export default function PhoneInfo({ caseInfo, isAdmin }: PhoneInfoProps) { const session = useSession(); + const userId = session?.user?.id; const { - data: assignments, + data: solicitudesUser, isLoading, error, - } = useQuery({ - queryKey: ['help_request_assignments', { id: caseInfo.id }], - queryFn: () => getAssignments(caseInfo.id), + } = useQuery({ + queryKey: ['help_requests', { user_id: userId, type: 'necesita' }], + queryFn: () => getSolicitudesWAssignemntsByUser(userId || ''), }); - if (error || isLoading) return <>; + if (error || isLoading || !solicitudesUser) return <>; - const userAssignment = assignments?.find((x) => x.user_id === session.user?.id); + const userAssignment = solicitudesUser.find((x) => x.id === caseInfo.id); + const userIsAssigned = !!userAssignment; return ( @@ -29,7 +31,7 @@ export default function PhoneInfo({ caseInfo, isAdmin }: PhoneInfoProps) { {session && session.user ? isAdmin ? caseInfo.contact_info - : !!userAssignment + : !!userIsAssigned ? caseInfo.contact_info : 'Dale al botón "Quiero ayudar" para ver sus datos de contacto.' : 'Inicia sesion para ver este dato'} diff --git a/src/components/map/map.tsx b/src/components/map/map.tsx index 1dae55d..b6e6383 100644 --- a/src/components/map/map.tsx +++ b/src/components/map/map.tsx @@ -1,17 +1,16 @@ 'use client'; -import { Dispatch, FC, ReactNode, SetStateAction, useState } from 'react'; +import { Dispatch, FC, SetStateAction, useState } from 'react'; import InteractiveMap, { Layer, MapLayerMouseEvent, Source } from 'react-map-gl/maplibre'; import 'maplibre-gl/dist/maplibre-gl.css'; import { useModal } from '@/context/ModalProvider'; -import { HelpRequestData } from '@/types/Requests'; -import { InterpolationSpecification } from 'maplibre-gl'; +import { HelpRequestWAssignments } from '@/types/Requests'; export const MAP_MODAL_NAME = `map-marker`; type MapProps = { solicitudes?: GeoJSON.FeatureCollection; - setSelectedMarker: Dispatch>; + setSelectedMarker: Dispatch>; }; const PAIPORTA_LAT = 39.42333; @@ -30,7 +29,7 @@ const Map: FC = ({ solicitudes, setSelectedMarker }) => { const onClickHandler = (e: MapLayerMouseEvent) => { if (e.features?.[0]) { toggleModal(MAP_MODAL_NAME, true); - setSelectedMarker(e.features?.[0].properties as HelpRequestData); + setSelectedMarker(e.features?.[0].properties as HelpRequestWAssignments); } }; diff --git a/src/components/solicitudes/SolicitudCard.tsx b/src/components/solicitudes/SolicitudCard.tsx index 2654ad7..3b5f8e8 100644 --- a/src/components/solicitudes/SolicitudCard.tsx +++ b/src/components/solicitudes/SolicitudCard.tsx @@ -1,10 +1,9 @@ -import { AlertTriangle, MapPin, MapPinned, Megaphone, Phone, Users } from 'lucide-react'; +import { AlertTriangle, MapPinned, Megaphone, Phone, Users } from 'lucide-react'; import { tiposAyudaOptions } from '@/helpers/constants'; import Link from 'next/link'; import { useSession } from '@/context/SessionProvider'; -import { HelpRequestAdditionalInfo, HelpRequestData, SelectedHelpData } from '@/types/Requests'; +import { HelpRequestAdditionalInfo, SelectedHelpDataWAssignment } from '@/types/Requests'; import AsignarSolicitudButton from '@/components/AsignarSolicitudButton'; -import SolicitudHelpCount from '@/components/solicitudes/SolicitudHelpCount'; import PhoneInfo from '@/components/PhoneInfo'; import DeleteHelpRequest from '../DeleteHelpRequest'; import { textWithEllipsis } from '@/helpers/utils'; @@ -20,7 +19,7 @@ import CRMLog from '@/components/CRMLog'; import { getHighlightedText } from '@/helpers/format'; type SolicitudCardProps = { - caso: SelectedHelpData; + caso: SelectedHelpDataWAssignment; showLink?: boolean; showEdit?: boolean; format?: 'small' | 'large'; @@ -79,7 +78,11 @@ export default function SolicitudCard({
- +
+ {caso.assignments_count} VOLUNTARIOS +
({ - queryKey: ['help_request_assignments', { id: id }], - queryFn: () => getAssignments(id), - }); - - if (isLoading) return ; - - if (error || assignments === undefined) return <>; - - const volunteers = assignments.length; - - let colorClass: string; - - if (volunteers === 0) { - colorClass = 'bg-red-100 text-red-800'; - } else { - colorClass = 'bg-green-100 text-green-800'; - } - return ( -
- {volunteers} VOLUNTARIOS -
- ); -} diff --git a/src/components/solicitudes/SolicitudList.tsx b/src/components/solicitudes/SolicitudList.tsx index 4266411..5cde924 100644 --- a/src/components/solicitudes/SolicitudList.tsx +++ b/src/components/solicitudes/SolicitudList.tsx @@ -6,14 +6,14 @@ import { tiposAyudaOptions } from '@/helpers/constants'; import { useTowns } from '@/context/TownProvider'; import { Toggle } from '@/components/Toggle'; import TabNavigation, { TabNavigationCount } from '@/components/TabNavigation'; -import { HelpRequestData } from '@/types/Requests'; +import { SelectedHelpDataWAssignment } from '@/types/Requests'; import { Virtuoso } from 'react-virtuoso'; import { FiltersData, FilterType } from '@/app/casos-activos/solicitudes/types'; export const isStringTrue = (str: string): boolean => str === 'true'; type SolicitudListProps = { - data: HelpRequestData[]; + data: SelectedHelpDataWAssignment[]; count: TabNavigationCount; filtersData: FiltersData; onDataFilterChange: (type: FilterType, newFilter: string) => void; @@ -130,7 +130,7 @@ export default function SolicitudList({ data, count, filtersData, onDataFilterCh format="small" showLink={true} showEdit={true} - caso={caso as HelpRequestData} + caso={caso} highlightedText={filtersData.search} />
diff --git a/src/components/solicitudes/SolicitudMap.tsx b/src/components/solicitudes/SolicitudMap.tsx index 54b50e4..04ddff3 100644 --- a/src/components/solicitudes/SolicitudMap.tsx +++ b/src/components/solicitudes/SolicitudMap.tsx @@ -1,7 +1,7 @@ 'use client'; import Map from '@/components/map/map'; -import { HelpRequestData } from '@/types/Requests'; +import { HelpRequestData, HelpRequestWAssignments } from '@/types/Requests'; import { Dispatch, SetStateAction, useMemo } from 'react'; function transformHelpRequestToPointFeature(request: any): GeoJSON.Feature | [] { @@ -20,7 +20,7 @@ function transformHelpRequestToPointFeature(request: any): GeoJSON.Feature>; + setSelectedMarker: Dispatch>; }; export default function SolicitudList({ data, setSelectedMarker }: SolicitudListProps) { diff --git a/src/context/TownProvider.tsx b/src/context/TownProvider.tsx index 495c730..897b1f5 100644 --- a/src/context/TownProvider.tsx +++ b/src/context/TownProvider.tsx @@ -3,8 +3,29 @@ import { useQuery } from '@tanstack/react-query'; import { Town } from '@/types/Town'; import { getTowns } from '@/lib/actions'; +import { createContext, ReactNode, useContext, useEffect, useState } from 'react'; -export const useTowns = () => { +export type TownContextData = { + towns: Town[]; + isLoading: boolean; + error: Error | null; + getTownById: (id: number) => Town | null; +}; + +const TownContext = createContext({ + towns: [], + isLoading: false, + error: null, + getTownById: () => { + return null; + }, +}); + +type TownProviderProps = { + children: ReactNode; +}; + +export const TownProvider: React.FC = ({ children }) => { const { data: towns, isLoading, @@ -14,7 +35,13 @@ export const useTowns = () => { queryFn: () => getTowns(), }); - const getTownById = (id: number) => towns?.find((t) => t.id === id); + const getTownById = (id: number): Town | null => towns?.find((t) => t.id === id) || null; - return { towns: towns ?? [], isLoading, error, getTownById }; + return ( + + {children} + + ); }; + +export const useTowns = () => useContext(TownContext); diff --git a/src/helpers/format.tsx b/src/helpers/format.tsx index 0f91b07..5666b6d 100644 --- a/src/helpers/format.tsx +++ b/src/helpers/format.tsx @@ -1,23 +1,6 @@ -import ReactDOMServer from 'react-dom/server'; import { HelpRequestData } from '@/types/Requests'; -import SolicitudCard from '@/components/solicitudes/SolicitudCard'; import { Fragment } from 'react'; -export const getMarkerBySolicitud = (solicitud: HelpRequestData) => { - // TODO think if possible getLatLng from a given location - if (!solicitud.latitude || !solicitud.longitude) { - return null; - } - - return { - id: solicitud.id, - coordinates: [solicitud.longitude, solicitud.latitude], - descriptionHTML: getMarkerDescriptionBySolicitudAndTowns(solicitud), - color: getMarkerColorBySolicitud(solicitud), - width: '400px', - }; -}; - export const getMarkerColorBySolicitud = (solicitud: HelpRequestData) => { switch (solicitud.urgency) { case 'baja': @@ -30,11 +13,6 @@ export const getMarkerColorBySolicitud = (solicitud: HelpRequestData) => { return '#000000'; } }; -export const getMarkerDescriptionBySolicitudAndTowns = (solicitud: HelpRequestData) => { - return ReactDOMServer.renderToString( - , - ); -}; export const getHighlightedText = (text: string, highlight: string) => { if (highlight === '') return text; @@ -53,4 +31,4 @@ export const getHighlightedText = (text: string, highlight: string) => { ); }); -}; \ No newline at end of file +}; diff --git a/src/helpers/hooks.ts b/src/helpers/hooks.ts index 3487815..d66868f 100644 --- a/src/helpers/hooks.ts +++ b/src/helpers/hooks.ts @@ -43,18 +43,18 @@ export function useDebouncedFunction(callback: CallbackFunction export function useDebouncedValue(value: T, delay: number, options?: { signal: AbortSignal }): T { // State and setters for debounced value - const [debouncedValue, setDebouncedValue] = useState(value) + const [debouncedValue, setDebouncedValue] = useState(value); useEffect(() => { // Set debouncedValue to value (passed in) after the specified delay const debounced = debounce( () => { - setDebouncedValue(value) + setDebouncedValue(value); }, delay, - options - ) - debounced() + options, + ); + debounced(); // Return a cleanup function that will be called every time ... // ... useEffect is re-called. useEffect will only be re-called ... @@ -65,9 +65,9 @@ export function useDebouncedValue(value: T, delay: number, options?: { signal // ... search box, we don't want the debouncedValue to update until ... // ... they've stopped typing for more than 500ms. return () => { - debounced.cancel() - } - }, [delay, options, value]) // ... need to be able to change that dynamically. // You could also add the "delay" var to inputs array if you ... // Only re-call effect if value changes + debounced.cancel(); + }; + }, [delay, options, value]); // ... need to be able to change that dynamically. // You could also add the "delay" var to inputs array if you ... // Only re-call effect if value changes - return debouncedValue -} \ No newline at end of file + return debouncedValue; +} diff --git a/src/lib/actions.ts b/src/lib/actions.ts index c7be33f..d95e4ea 100644 --- a/src/lib/actions.ts +++ b/src/lib/actions.ts @@ -3,6 +3,7 @@ import { CRMUsersLogRow, helpDataSelectFields, + helpDataWithAssignmentsSelectFields, HelpRequestAssignmentInsert, HelpRequestData, HelpRequestInsert, @@ -10,6 +11,7 @@ import { PuntoDeEntrega, PuntoDeRecogida, SelectedHelpData, + SelectedHelpDataWAssignment, } from '@/types/Requests'; import { createClient } from './supabase/server'; import { AuthChangeEvent, Session, SignInWithOAuthCredentials, Subscription } from '@supabase/supabase-js'; @@ -117,6 +119,17 @@ export async function getOne(id: number) { return data as SelectedHelpData; } +export async function getOneWithAssignments(id: number) { + const supabase = await createClient(); + const { data, error } = await supabase + .from('help_requests_with_assignment_count') + .select(helpDataSelectFields as '*') + .eq('id', id) + .single(); + if (error) throw error; + return data as SelectedHelpDataWAssignment; +} + export async function getOneWithCoords(id: number) { const supabase = await createClient(); const { data, error } = await supabase.from('help_requests').select('*').eq('id', id).single(); @@ -204,6 +217,18 @@ export async function getSolicitudesByUser(user_id: string | undefined) { return requests as SelectedHelpData[]; } +export async function getSolicitudesWAssignemntsByUser(user_id: string | undefined) { + const supabase = await createClient(); + if (!user_id) return []; + const { data: requests, error: requestsError } = await supabase + .from('help_requests_with_assignment_count') + .select(helpDataWithAssignmentsSelectFields as '*') + .eq('type', 'necesita') + .eq('user_id', user_id); + if (requestsError) throw requestsError; + return requests as SelectedHelpDataWAssignment[]; +} + export async function getAssignments(id: number) { const supabase = await createClient(); const { data, error } = await supabase.from('help_request_assignments').select('*').eq('help_request_id', id); diff --git a/src/types/Requests.ts b/src/types/Requests.ts index 0e4046d..8bada0c 100644 --- a/src/types/Requests.ts +++ b/src/types/Requests.ts @@ -13,6 +13,7 @@ export type HelpRequestData = Omit; export type SelectedHelpData = OmitSelect; +export type SelectedHelpDataWAssignment = OmitSelect< + HelpRequestWAssignments, + 'location' | 'coordinates' | 'latitude' | 'longitude' +>; + export const helpDataSelectFieldsObject: SelectHelpDataStringBuilder = { additional_info: true, asignees_count: true, @@ -89,3 +95,6 @@ export const helpDataSelectFieldsObject: SelectHelpDataStringBuilder = { user_id: true, }; export const helpDataSelectFields = Object.keys(helpDataSelectFieldsObject).join(','); +export const helpDataWithAssignmentsSelectFields = Object.keys(helpDataSelectFieldsObject) + .join(',') + .concat(', assignments_count');