Skip to content

Commit

Permalink
link selected tag to shown subscriptions
Browse files Browse the repository at this point in the history
  • Loading branch information
vorant94 committed Jun 17, 2024
1 parent 98a2205 commit bd71ac8
Show file tree
Hide file tree
Showing 7 changed files with 97 additions and 53 deletions.
5 changes: 4 additions & 1 deletion src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
type NavLink,
} from '@/router/providers/nav-links.provider.tsx';
import { SubscriptionUpsertStateProvider } from '@/subscriptions/providers/subscription-upsert-state.provider';
import { SubscriptionsProvider } from '@/subscriptions/providers/subscriptions.provider.tsx';
import { DefaultLayoutContextProvider } from '@/ui/layouts/default.layout';
import { faChartSimple, faCreditCard } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
Expand All @@ -14,7 +15,9 @@ export const App = memo(() => {
<NavLinksContextProvider navLinks={navLinks}>
<DefaultLayoutContextProvider>
<SubscriptionUpsertStateProvider>
<Outlet />
<SubscriptionsProvider>
<Outlet />
</SubscriptionsProvider>
</SubscriptionUpsertStateProvider>
</DefaultLayoutContextProvider>
</NavLinksContextProvider>
Expand Down
7 changes: 6 additions & 1 deletion src/dashboard/pages/dashboard.page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { AddSubscriptionButton } from '@/subscriptions/components/add-subscripti
import { SubscriptionUpsert } from '@/subscriptions/components/subscription-upsert.tsx';
import { SubscriptionsByMonthChart } from '@/subscriptions/components/subscriptions-by-month-chart.tsx';
import { SubscriptionUpsertStateContext } from '@/subscriptions/providers/subscription-upsert-state.provider.tsx';
import { TagSelect } from '@/tags/components/tag-select.tsx';
import {
DefaultLayout,
DefaultLayoutHeader,
Expand All @@ -14,7 +15,11 @@ export const DashboardPage = memo(() => {

return (
<DefaultLayout
header={<DefaultLayoutHeader actions={<AddSubscriptionButton />} />}
header={
<DefaultLayoutHeader actions={<AddSubscriptionButton />}>
<TagSelect />
</DefaultLayoutHeader>
}
drawerContent={<SubscriptionUpsert />}
drawerTitle={`${upsert.state.mode === 'update' ? 'Update' : 'Insert'} Subscription`}>
<div
Expand Down
7 changes: 3 additions & 4 deletions src/subscriptions/components/subscription-list.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import { cn } from '@/ui/utils/cn.ts';
import { useLiveQuery } from 'dexie-react-hooks';
import { memo } from 'react';
import { findSubscriptions } from '../models/subscription.table.ts';
import { memo, useContext } from 'react';
import { SubscriptionsContext } from '../providers/subscriptions.provider.tsx';
import { SubscriptionListItem } from './subscription-list-item.tsx';

export const SubscriptionList = memo(() => {
const subscriptions = useLiveQuery(() => findSubscriptions());
const { subscriptions } = useContext(SubscriptionsContext);

return (
<div className={cn(`grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-4`)}>
Expand Down
7 changes: 3 additions & 4 deletions src/subscriptions/components/subscriptions-by-month-chart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,14 @@ import {
} from '@/date/types/month.ts';
import { cn } from '@/ui/utils/cn.ts';
import { Card } from '@mantine/core';
import { useLiveQuery } from 'dexie-react-hooks';
import { memo, useMemo } from 'react';
import { memo, useContext, useMemo } from 'react';
import { Bar, BarChart, ResponsiveContainer, XAxis } from 'recharts';
import type { SubscriptionModel } from '../models/subscription.model.tsx';
import { findSubscriptions } from '../models/subscription.table.ts';
import { SubscriptionsContext } from '../providers/subscriptions.provider.tsx';
import { cyclePeriodToCalculateMonthlyPrice } from '../utils/cycle-period-to-calculate-monthly-price.ts';

export const SubscriptionsByMonthChart = memo(() => {
const subscriptions = useLiveQuery(() => findSubscriptions());
const { subscriptions } = useContext(SubscriptionsContext);

const aggregatedSubscriptions = useMemo(
() => Object.entries(aggregateSubscriptionsByMonth(subscriptions ?? [])),
Expand Down
16 changes: 3 additions & 13 deletions src/subscriptions/pages/subscriptions.page.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
import {
TagSelect,
type TagsSelectProps,
} from '@/tags/components/tag-select.tsx';
import { TagSelect } from '@/tags/components/tag-select.tsx';
import {
DefaultLayout,
DefaultLayoutHeader,
} from '@/ui/layouts/default.layout.tsx';
import { memo, useCallback, useContext } from 'react';
import { memo, useContext } from 'react';
import { AddSubscriptionButton } from '../components/add-subscription-button.tsx';
import { SubscriptionList } from '../components/subscription-list.tsx';
import { SubscriptionUpsert } from '../components/subscription-upsert.tsx';
Expand All @@ -15,18 +12,11 @@ import { SubscriptionUpsertStateContext } from '../providers/subscription-upsert
export const SubscriptionsPage = memo(() => {
const upsert = useContext(SubscriptionUpsertStateContext);

const updateSelectedTags: TagsSelectProps['onChange'] = useCallback(
(tags) => {
console.log(tags);
},
[],
);

return (
<DefaultLayout
header={
<DefaultLayoutHeader actions={<AddSubscriptionButton />}>
<TagSelect onChange={updateSelectedTags} />
<TagSelect />
</DefaultLayoutHeader>
}
drawerContent={<SubscriptionUpsert />}
Expand Down
66 changes: 66 additions & 0 deletions src/subscriptions/providers/subscriptions.provider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import type { TagModel } from '@/tags/models/tag.model.ts';
import { findTags } from '@/tags/models/tag.table.ts';
import { useLiveQuery } from 'dexie-react-hooks';
import {
createContext,
memo,
useCallback,
useMemo,
useState,
type PropsWithChildren,
} from 'react';
import type { SubscriptionModel } from '../models/subscription.model.tsx';
import { findSubscriptions } from '../models/subscription.table.ts';

export const SubscriptionsProvider = memo(({ children }: PropsWithChildren) => {
const unfilteredSubscriptions = useLiveQuery(() => findSubscriptions());
const tags = useLiveQuery(() => findTags());
const [selectedTag, setSelectedTag] = useState<TagModel | null>(null);

const selectTag: (tagId: string | null) => void = useCallback(
(tagId) => {
if (tagId) {
setSelectedTag(
(tags ?? []).find((tag) => `${tag.id}` === tagId) ?? null,
);
} else {
setSelectedTag(null);
}
},
[tags],
);

const subscriptions = useMemo(
() =>
(unfilteredSubscriptions ?? []).filter(
(subscription) =>
!selectedTag ||
subscription.tags.find((subTag) => subTag.id === selectedTag.id),
),
[selectedTag, unfilteredSubscriptions],
);

return (
<SubscriptionsContext.Provider
value={{
subscriptions,
tags: tags ?? [],
selectedTag,
selectTag,
}}>
{children}
</SubscriptionsContext.Provider>
);
});

export const SubscriptionsContext = createContext<{
subscriptions: Array<SubscriptionModel>;
tags: Array<TagModel>;
selectedTag: TagModel | null;
selectTag(tagId: string | null): void;
}>({
subscriptions: [],
tags: [],
selectedTag: null,
selectTag() {},
});
42 changes: 12 additions & 30 deletions src/tags/components/tag-select.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { SubscriptionsContext } from '@/subscriptions/providers/subscriptions.provider.tsx';
import { cn } from '@/ui/utils/cn.ts';
import { faCircle, faSliders } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
Expand All @@ -9,38 +10,23 @@ import {
InputBase,
useCombobox,
} from '@mantine/core';
import { useDisclosure } from '@mantine/hooks';
import { useLiveQuery } from 'dexie-react-hooks';
import { memo, useCallback, useEffect, useState } from 'react';
import type { TagModel } from '../models/tag.model.ts';
import { findTags } from '../models/tag.table.ts';
import { useDisclosure, usePrevious } from '@mantine/hooks';
import { memo, useContext, useEffect } from 'react';
import { ManageTagsModal } from './manage-tags-modal.tsx';

export const TagSelect = memo(({ onChange }: TagsSelectProps) => {
const tags = useLiveQuery(() => findTags());

const [selectedTag, setSelectedTag] = useState<TagModel | null>(null);
useEffect(() => {
onChange(selectedTag);
}, [onChange, selectedTag]);
export const TagSelect = memo(() => {
const { tags, selectTag, selectedTag } = useContext(SubscriptionsContext);
const prevSelectedTag = usePrevious(selectedTag);

const combobox = useCombobox({
onDropdownClose: () => combobox.resetSelectedOption(),
});
const setSelectedTagById: (tagId: string | null) => void = useCallback(
(tagId) => {
if (tagId) {
setSelectedTag(
(tags ?? []).find((tag) => `${tag.id}` === tagId) ?? null,
);
} else {
setSelectedTag(null);
}

useEffect(() => {
if (selectedTag?.id !== prevSelectedTag?.id) {
combobox.closeDropdown();
},
[combobox, tags],
);
}
}, [selectedTag, prevSelectedTag, combobox]);

const [isManageTagsOpen, manageTags] = useDisclosure(false);

Expand All @@ -49,7 +35,7 @@ export const TagSelect = memo(({ onChange }: TagsSelectProps) => {
<div className={cn(`flex items-center gap-2`)}>
<Combobox
store={combobox}
onOptionSubmit={setSelectedTagById}>
onOptionSubmit={selectTag}>
<Combobox.Target>
<InputBase
className={cn(`w-48`)}
Expand All @@ -69,7 +55,7 @@ export const TagSelect = memo(({ onChange }: TagsSelectProps) => {
<CloseButton
size="sm"
onMouseDown={(event) => event.preventDefault()}
onClick={() => setSelectedTagById(null)}
onClick={() => selectTag(null)}
aria-label="Clear value"
/>
) : (
Expand Down Expand Up @@ -120,7 +106,3 @@ export const TagSelect = memo(({ onChange }: TagsSelectProps) => {
</>
);
});

export interface TagsSelectProps {
onChange(tags: TagModel | null): void;
}

0 comments on commit bd71ac8

Please sign in to comment.