Skip to content

Commit

Permalink
Update Collective profile prototype
Browse files Browse the repository at this point in the history
  • Loading branch information
gustavlrsn committed Dec 2, 2024
1 parent a47ae3d commit 5352159
Show file tree
Hide file tree
Showing 54 changed files with 877 additions and 2,160 deletions.
448 changes: 0 additions & 448 deletions components/crowdfunding-redesign/Accounts.tsx

This file was deleted.

30 changes: 1 addition & 29 deletions components/crowdfunding-redesign/AccountsList.tsx
Original file line number Diff line number Diff line change
@@ -1,32 +1,8 @@
import React from 'react';
import type { Column, ColumnDef, Row, TableMeta } from '@tanstack/react-table';
import clsx from 'clsx';
import { isNil, omit } from 'lodash';
import { ArrowDown10, ArrowDownZA, ArrowUp10, ArrowUpZA } from 'lucide-react';
import { FormattedMessage } from 'react-intl';

import type {
AccountMetricsFragment,
Currency,
OverviewMetricsQueryVariables,
} from '../../lib/graphql/types/v2/graphql';
import type { useQueryFilterReturnType } from '../../lib/hooks/useQueryFilter';
import { getCollectivePageRoute } from '../../lib/url-helpers';

import { AccountHoverCard } from '../AccountHoverCard';
import Avatar from '../Avatar';
import type { schema } from '../dashboard/sections/overview/CollectiveOverview';
import type { MetricProps } from '../dashboard/sections/overview/Metric';
import { ChangeBadge, getPercentageDifference } from '../dashboard/sections/overview/Metric';
import FormattedMoneyAmount from '../FormattedMoneyAmount';
import Link from '../Link';
import { DataTable } from '../table/DataTable';
import { Badge } from '../ui/Badge';
import { Button } from '../ui/Button';
import { Checkbox } from '../ui/Checkbox';
import { AccountsSublist } from './AccountsSublist';

export default function AccountsList({ data, queryFilter, loading, metric }) {
export default function AccountsList({ data, queryFilter, metric }) {
const currency = data?.account?.[metric.id]?.current?.currency;

const meta = {
Expand All @@ -36,10 +12,6 @@ export default function AccountsList({ data, queryFilter, loading, metric }) {
metric,
};

// if (error) {
// return <MessageBoxGraphqlError error={error} />;
// }

return (
<div className="space-y-8">
<AccountsSublist label="Main account" type="COLLECTIVE" data={data} metric={metric} meta={meta} />
Expand Down
184 changes: 22 additions & 162 deletions components/crowdfunding-redesign/AccountsSublist.tsx
Original file line number Diff line number Diff line change
@@ -1,165 +1,23 @@
import React from 'react';
import type { Column, ColumnDef, Row, TableMeta } from '@tanstack/react-table';
import clsx from 'clsx';
import { isNil, omit } from 'lodash';
import { ArrowDown10, ArrowDownZA, ArrowUp10, ArrowUpZA, ChevronRight } from 'lucide-react';
import { FormattedMessage } from 'react-intl';
import { ArrowUp10, ChevronRight } from 'lucide-react';
import { useRouter } from 'next/router';

import type {
AccountMetricsFragment,
Currency,
OverviewMetricsQueryVariables,
} from '../../lib/graphql/types/v2/graphql';
import type { useQueryFilterReturnType } from '../../lib/hooks/useQueryFilter';
import { getCollectivePageRoute } from '../../lib/url-helpers';
import type { AccountMetricsFragment } from '../../lib/graphql/types/v2/graphql';

import { AccountHoverCard } from '../AccountHoverCard';
import Avatar from '../Avatar';
import type { schema } from '../dashboard/sections/overview/CollectiveOverview';
import type { MetricProps } from '../dashboard/sections/overview/Metric';
import { ChangeBadge, getPercentageDifference } from '../dashboard/sections/overview/Metric';
import { getPercentageDifference } from '../dashboard/sections/overview/Metric';
import FormattedMoneyAmount from '../FormattedMoneyAmount';
import Link from '../Link';
import { DataTable } from '../table/DataTable';
import { Badge } from '../ui/Badge';
import { Button } from '../ui/Button';
import { Checkbox } from '../ui/Checkbox';
import { useRouter } from 'next/router';

import { triggerPrototypeToast } from './helpers';

type AccountMetricsRow = AccountMetricsFragment & {
current: number;
comparison?: number;
percentageDifference?: number;
};

interface AccountMetricsMeta extends TableMeta<AccountMetricsRow> {
currency: Currency;
isAmount: boolean;
queryFilter: useQueryFilterReturnType<typeof schema, OverviewMetricsQueryVariables>;
metric: MetricProps;
}

const SortableHeader = ({
column,
label,
type,
align,
}: {
column: Column<AccountMetricsRow, unknown>;
label: React.ReactNode;
type?: 'alphabetic' | 'numerical';
align?: 'left' | 'right';
}) => {
const isSorted = column.getIsSorted();
const isSortedDesc = isSorted === 'desc';
const UpIcon = type === 'alphabetic' ? ArrowUpZA : ArrowUp10;
const DownIcon = type === 'alphabetic' ? ArrowDownZA : ArrowDown10;
const SortIcon = isSortedDesc || !isSorted ? UpIcon : DownIcon;
return (
<div className={clsx('flex items-center', align === 'right' && 'justify-end')}>
<Button
variant="ghost"
size="xs"
className={clsx('group/btn -m-2 gap-2', isSorted && 'text-foreground')}
onClick={() => column.toggleSorting(!isSortedDesc)}
>
<SortIcon
className={clsx(
'h-4 w-4 transition-colors',
isSorted ? 'text-muted-foreground' : 'text-transparent group-hover/btn:text-muted-foreground',
)}
/>
<span className={clsx(align === 'left' && '-order-1')}>{label}</span>
</Button>
</div>
);
};
const columns: ColumnDef<AccountMetricsRow>[] = [
{
id: 'name',
accessorKey: 'name',
header: ({ column }) => (
<SortableHeader
column={column}
type="alphabetic"
label={<FormattedMessage defaultMessage="Name" id="HAlOn1" />}
align="left"
/>
),
meta: { className: 'min-w-0 max-w-[300px]' },

cell: ({ row, table }) => {
const account = row.original;
// const { queryFilter } = table.options.meta as AccountMetricsMeta;
// const selectedAccountSlug = queryFilter.values.account;
return (
<div className="flex items-center gap-3 text-base">
<div className="flex items-center gap-1.5 overflow-hidden">
<AccountHoverCard
account={account}
trigger={
<div className="max-w-[400px] truncate">
<Link
href={getCollectivePageRoute(account)}
className={clsx('truncate hover:underline group-hover/row:text-foreground')}
>
{account.name}
</Link>
</div>
}
/>

{account.isArchived && (
<Badge size="xs" className="capitalize">
Archived {account.type.toLowerCase()}
</Badge>
)}
</div>
</div>
);
},
},
{
id: 'current',
accessorKey: 'current',
meta: { className: 'text-right' },
header: ({ column, table }) => {
const meta = table.options.meta as AccountMetricsMeta;
return <SortableHeader align="right" column={column} label={meta.metric.label} />;
},
sortingFn: (rowA: Row<AccountMetricsRow>, rowB: Row<AccountMetricsRow>): number => {
const a = rowA.original.current;
const b = rowB.original.current;

const diff = a - b;

// sort by comparison value if current is the same
if (diff === 0) {
const rowAPrevious = rowA.original.comparison;
const rowBPrevious = rowB.original.comparison;
return rowAPrevious - rowBPrevious;
}
return a - b;
},
cell: ({ cell, table }) => {
const current = cell.getValue() as number;
const meta = table.options.meta as AccountMetricsMeta;

return (
<div className="flex items-center justify-end gap-2">
<span className="text-base font-medium">
{meta.isAmount ? (
<FormattedMoneyAmount amount={current} currency={meta.currency} precision={2} showCurrencyCode={false} />
) : (
current.toLocaleString()
)}
</span>
<ChevronRight size={20} className="text-muted-foreground" />
</div>
);
},
},
];

export function AccountsSublist({ label, type, data, metric, meta }) {
const router = useRouter();
const columnData: AccountMetricsRow[] = React.useMemo(() => {
Expand All @@ -178,10 +36,23 @@ export function AccountsSublist({ label, type, data, metric, meta }) {
percentageDifference: getPercentageDifference(current, comparison),
};
});
}, [metric.id, data]);
}, [metric.id, data, type]);

return (
<div className="">
<h2 className="mb-3 px-2 text-lg font-semibold text-slate-800">{label}</h2>
<div className="mb-2 flex items-center justify-between gap-4 px-2">
<h2 className="text-lg font-semibold text-slate-800">{label}</h2>
<Button
variant="ghost"
size="sm"
className={'group/btn -m-2 gap-2 text-foreground'}
onClick={triggerPrototypeToast}
disabled={columnData.length === 1}
>
<span>{metric.id === 'balance' ? 'Balance' : metric.label}</span>
<ArrowUp10 className="h-4 w-4 text-muted-foreground transition-colors" />
</Button>
</div>
<div className="flex flex-col divide-y overflow-hidden rounded-xl border bg-background">
{columnData
.sort((a, b) => b.current - a.current)
Expand All @@ -206,17 +77,6 @@ export function AccountsSublist({ label, type, data, metric, meta }) {
</Link>
))}
</div>

{/* <DataTable
hideHeader
className="bg-background"
columns={columns}
data={columnData}
initialSort={[{ id: 'current', desc: true }]}
nbPlaceholders={nbPlaceholders}
loading={loading}
meta={meta}
/> */}
</div>
);
}
31 changes: 1 addition & 30 deletions components/crowdfunding-redesign/Breadcrumb.tsx
Original file line number Diff line number Diff line change
@@ -1,36 +1,7 @@
import { useRouter } from 'next/router';
import React from 'react';
import Link from '../Link';
import { Slash } from 'lucide-react';

const getPathdata = (router, collective, account) => {
switch (router.pathname) {
case '/preview/[collectiveSlug]/finances/[accountSlug]':
return [{ href: `/preview/${router.query.collectiveSlug}/finances`, label: 'Finances' }];
case '/preview/[collectiveSlug]/transactions/[groupId]':
return [
{ href: `/preview/${router.query.collectiveSlug}/finances`, label: 'Finances' },
{
href: `/preview/${router.query.collectiveSlug}/finances/${router.query.collectiveSlug}`,
label: collective?.name,
},
];
case '/preview/[collectiveSlug]/[accountSlug]/transactions/[groupId]':
return [
{ href: `/preview/${router.query.collectiveSlug}/finances`, label: 'Finances' },
{
href: `/preview/${router.query.collectiveSlug}/finances/${router.query.accountSlug}`,
label: account?.name,
},
];
case '/preview/[collectiveSlug]/projects/[accountSlug]':
return [{ href: `/preview/${router.query.collectiveSlug}/projects`, label: 'Projects' }];
case '/preview/[collectiveSlug]/events/[accountSlug]':
return [{ href: `/preview/${router.query.collectiveSlug}/events`, label: 'Events' }];
default:
return [{ href: '', label: '' }];
}
};
import Link from '../Link';

export function Breadcrumb({ breadcrumbs }) {
return (
Expand Down
40 changes: 0 additions & 40 deletions components/crowdfunding-redesign/CollectiveHeader.tsx

This file was deleted.

31 changes: 24 additions & 7 deletions components/crowdfunding-redesign/ContentOverview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import React, { useEffect, useState } from 'react';
import { cva } from 'class-variance-authority';
// eslint-disable-next-line no-restricted-imports
import Link from 'next/link';
import sanitizeHtml from 'sanitize-html';

import { triggerPrototypeToast } from './helpers';

export const ContentOverview = ({ content }) => {
Expand All @@ -15,10 +17,10 @@ export const ContentOverview = ({ content }) => {
setHeadings(headingTexts);
}, [content]);

const linkClasses = cva('px-2 font-semibold block hover:text-primary text-sm border-l-[3px]', {
const linkClasses = cva('block border-l-[3px] px-2 text-sm font-semibold hover:text-primary', {
variants: {
active: {
true: 'border-primary/70',
true: 'border-primary/80',
false: 'border-transparent',
},
},
Expand All @@ -29,11 +31,26 @@ export const ContentOverview = ({ content }) => {

return (
<div className="space-y-4">
{headings.map(heading => (
<Link href="#" key={heading} className={linkClasses()} onClick={triggerPrototypeToast}>
{heading}
</Link>
))}
<Link href="#" className={linkClasses({ active: true })} onClick={triggerPrototypeToast}>
About
</Link>
{headings.map(heading => {
const sanitizedKey = sanitizeHtml(heading, {
allowedTags: [], // No tags allowed
allowedAttributes: {}, // No attributes allowed
}).replace(/[^a-zA-Z0-9-_]/g, '_'); // Replace unsafe characters

return (
<Link
href="#"
key={sanitizedKey} // Use sanitized and unique key
className={linkClasses()}
onClick={triggerPrototypeToast}
>
{heading}
</Link>
);
})}
</div>
);
};
Loading

0 comments on commit 5352159

Please sign in to comment.