Skip to content

Commit ea43064

Browse files
authored
Update Collective profile prototype (#10678)
1 parent 98b3bf2 commit ea43064

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

53 files changed

+2615
-1004
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import React from 'react';
2+
3+
import { AccountsSublist } from './AccountsSublist';
4+
5+
export default function AccountsList({ data, queryFilter, metric }) {
6+
const currency = data?.account?.[metric.id]?.current?.currency;
7+
8+
const meta = {
9+
queryFilter,
10+
currency: currency,
11+
isAmount: !!metric.amount,
12+
metric,
13+
};
14+
15+
return (
16+
<div className="space-y-8">
17+
<AccountsSublist label="Main account" type="COLLECTIVE" data={data} metric={metric} meta={meta} />
18+
<AccountsSublist label="Projects" type="PROJECT" data={data} metric={metric} meta={meta} />
19+
20+
<AccountsSublist label="Events" type="EVENT" data={data} metric={metric} meta={meta} />
21+
</div>
22+
);
23+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import React from 'react';
2+
import { isNil, omit } from 'lodash';
3+
import { ArrowUp10, ChevronRight } from 'lucide-react';
4+
import { useRouter } from 'next/router';
5+
6+
import type { AccountMetricsFragment } from '../../lib/graphql/types/v2/graphql';
7+
8+
import { getPercentageDifference } from '../dashboard/sections/overview/Metric';
9+
import FormattedMoneyAmount from '../FormattedMoneyAmount';
10+
import Link from '../Link';
11+
import { Button } from '../ui/Button';
12+
13+
import { triggerPrototypeToast } from './helpers';
14+
15+
type AccountMetricsRow = AccountMetricsFragment & {
16+
current: number;
17+
comparison?: number;
18+
percentageDifference?: number;
19+
};
20+
21+
export function AccountsSublist({ label, type, data, metric, meta }) {
22+
const router = useRouter();
23+
const columnData: AccountMetricsRow[] = React.useMemo(() => {
24+
const nodes = data
25+
? [omit(data?.account, 'childrenAccounts'), ...(data?.account.childrenAccounts.nodes ?? [])]
26+
: [];
27+
const filteredNodes = nodes.filter(node => node.type === type);
28+
29+
return filteredNodes.map(node => {
30+
const current = node[metric.id].current.valueInCents ?? node[metric.id].current;
31+
const comparison = node[metric.id].comparison?.valueInCents ?? node[metric.id].comparison;
32+
return {
33+
...node,
34+
current: Math.abs(current),
35+
comparison: !isNil(comparison) ? Math.abs(comparison) : undefined,
36+
percentageDifference: getPercentageDifference(current, comparison),
37+
};
38+
});
39+
}, [metric.id, data, type]);
40+
41+
return (
42+
<div className="">
43+
<div className="mb-2 flex items-center justify-between gap-4 px-2">
44+
<h2 className="text-lg font-semibold text-slate-800">{label}</h2>
45+
<Button
46+
variant="ghost"
47+
size="sm"
48+
className={'group/btn -m-2 gap-2 text-foreground'}
49+
onClick={triggerPrototypeToast}
50+
disabled={columnData.length === 1}
51+
>
52+
<span>{metric.id === 'balance' ? 'Balance' : metric.label}</span>
53+
<ArrowUp10 className="h-4 w-4 text-muted-foreground transition-colors" />
54+
</Button>
55+
</div>
56+
<div className="flex flex-col divide-y overflow-hidden rounded-xl border bg-background">
57+
{columnData
58+
.sort((a, b) => b.current - a.current)
59+
.map(account => (
60+
<Link
61+
key={account.id}
62+
className="flex items-center justify-between px-4 py-4 hover:bg-muted"
63+
href={`/preview/${router.query.collectiveSlug}/finances/${account.slug}`}
64+
>
65+
<div>{account.name}</div>
66+
<div className="flex items-center gap-2">
67+
<div className="font-medium">
68+
<FormattedMoneyAmount
69+
amount={account.current}
70+
currency={meta.currency}
71+
precision={2}
72+
showCurrencyCode={false}
73+
/>
74+
</div>
75+
<ChevronRight size={20} className="text-muted-foreground" />
76+
</div>
77+
</Link>
78+
))}
79+
</div>
80+
</div>
81+
);
82+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import React from 'react';
2+
import { Slash } from 'lucide-react';
3+
4+
import Link from '../Link';
5+
6+
export function Breadcrumb({ breadcrumbs }) {
7+
return (
8+
<div className="flex items-center gap-2 text-sm font-medium text-muted-foreground">
9+
<Slash size={20} strokeWidth={1} />
10+
11+
{breadcrumbs?.map(({ href, label }, i, a) => {
12+
if (i === a.length - 1) {
13+
return (
14+
<span key={href} className="p-1 text-foreground">
15+
{label}
16+
</span>
17+
);
18+
}
19+
return (
20+
<React.Fragment key={href}>
21+
<Link href={href} className="rounded p-1 hover:bg-muted hover:text-foreground">
22+
<span className="text-muted-foreground">{label}</span>
23+
</Link>
24+
<Slash size={20} strokeWidth={1} />
25+
</React.Fragment>
26+
);
27+
})}
28+
</div>
29+
);
30+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import React, { useEffect, useState } from 'react';
2+
import { cva } from 'class-variance-authority';
3+
// eslint-disable-next-line no-restricted-imports
4+
import Link from 'next/link';
5+
import sanitizeHtml from 'sanitize-html';
6+
7+
import { triggerPrototypeToast } from './helpers';
8+
9+
export const ContentOverview = ({ content }) => {
10+
const [headings, setHeadings] = useState<string[]>([]);
11+
12+
useEffect(() => {
13+
const parser = new DOMParser();
14+
const doc = parser.parseFromString(content, 'text/html');
15+
const headingElements = doc.querySelectorAll('h3');
16+
const headingTexts = Array.from(headingElements).map(h3 => h3.textContent?.trim() || '');
17+
setHeadings(headingTexts);
18+
}, [content]);
19+
20+
const linkClasses = cva('block border-l-[3px] px-2 text-sm font-semibold hover:text-primary', {
21+
variants: {
22+
active: {
23+
true: 'border-primary/80',
24+
false: 'border-transparent',
25+
},
26+
},
27+
defaultVariants: {
28+
active: false,
29+
},
30+
});
31+
32+
return (
33+
<div className="space-y-4">
34+
<Link href="#" className={linkClasses({ active: true })} onClick={triggerPrototypeToast}>
35+
About
36+
</Link>
37+
{headings.map(heading => {
38+
const sanitizedKey = sanitizeHtml(heading, {
39+
allowedTags: [], // No tags allowed
40+
allowedAttributes: {}, // No attributes allowed
41+
}).replace(/[^a-zA-Z0-9-_]/g, '_'); // Replace unsafe characters
42+
43+
return (
44+
<Link
45+
href="#"
46+
key={sanitizedKey} // Use sanitized and unique key
47+
className={linkClasses()}
48+
onClick={triggerPrototypeToast}
49+
>
50+
{heading}
51+
</Link>
52+
);
53+
})}
54+
</div>
55+
);
56+
};

components/crowdfunding-redesign/ContributionsList.tsx

-99
This file was deleted.

components/crowdfunding-redesign/CrowdfundingPrototypePage.tsx

-49
This file was deleted.

0 commit comments

Comments
 (0)