From d068e7a60d38df8fc5b1773ba8e72878d548d4c5 Mon Sep 17 00:00:00 2001 From: Teddy CHAMBARD Date: Tue, 20 Aug 2024 23:19:21 +0200 Subject: [PATCH] feat(solidr-front-next): migrate members tab components --- .../fronts/solidr-front-next/messages/en.json | 66 ++++++++++ .../components/SessionAddMemberDialog.tsx | 104 ++++++++++++++++ .../[sessionId]/components/SessionBalance.tsx | 115 ++++++++++++++++++ .../components/SessionEditMemberDialog.tsx | 91 ++++++++++++++ .../components/SessionExpenseSummary.tsx | 44 +++++++ .../[sessionId]/components/SessionInfo.tsx | 6 +- .../components/SessionInviteMemberDialog.tsx | 113 +++++++++++++++++ .../components/SessionMemberList.tsx | 94 ++++++++++++++ .../[sessionId]/join/[token]/page.tsx | 111 +++++++++++++++++ .../src/app/sessions/[sessionId]/page.tsx | 18 +-- .../src/app/sessions/create/page.tsx | 6 +- .../src/components/AddressAvatar/index.tsx | 37 ++++++ .../components/ConfirmationDialog/index.tsx | 101 ++++++--------- .../src/components/Dialog/index.tsx | 43 +++++++ .../src/components/PageTitleWrapper/index.tsx | 2 +- .../components/SectionTitleWrapper/index.tsx | 13 ++ .../solidr-front-next/src/services/colors.ts | 30 +++++ 17 files changed, 917 insertions(+), 77 deletions(-) create mode 100644 packages/fronts/solidr-front-next/src/app/sessions/[sessionId]/components/SessionAddMemberDialog.tsx create mode 100644 packages/fronts/solidr-front-next/src/app/sessions/[sessionId]/components/SessionBalance.tsx create mode 100644 packages/fronts/solidr-front-next/src/app/sessions/[sessionId]/components/SessionEditMemberDialog.tsx create mode 100644 packages/fronts/solidr-front-next/src/app/sessions/[sessionId]/components/SessionExpenseSummary.tsx create mode 100644 packages/fronts/solidr-front-next/src/app/sessions/[sessionId]/components/SessionInviteMemberDialog.tsx create mode 100644 packages/fronts/solidr-front-next/src/app/sessions/[sessionId]/components/SessionMemberList.tsx create mode 100644 packages/fronts/solidr-front-next/src/app/sessions/[sessionId]/join/[token]/page.tsx create mode 100644 packages/fronts/solidr-front-next/src/components/AddressAvatar/index.tsx create mode 100644 packages/fronts/solidr-front-next/src/components/Dialog/index.tsx create mode 100644 packages/fronts/solidr-front-next/src/components/SectionTitleWrapper/index.tsx create mode 100644 packages/fronts/solidr-front-next/src/services/colors.ts diff --git a/packages/fronts/solidr-front-next/messages/en.json b/packages/fronts/solidr-front-next/messages/en.json index b9db752..7dfa63c 100644 --- a/packages/fronts/solidr-front-next/messages/en.json +++ b/packages/fronts/solidr-front-next/messages/en.json @@ -55,6 +55,15 @@ } } }, + "join": { + "title": "Join session {name}", + "form": { + "memberName": { + "label": "Your member name", + "required": "Member name is required" + } + } + }, "action": { "close": { "menu": { @@ -83,6 +92,62 @@ } } }, + "summary": { + "totalCost": "My total cost", + "totalExpenses": "Total expenses", + "totalRefunds": "Total refunds" + }, + "share": { + "title": "Share", + "generatedUrl": { + "title": "Generated url" + }, + "generateLink": { + "title": "Generate new link" + } + }, + "members": { + "list": { + "title": "List of members" + }, + "add": { + "title": "Add a new member", + "form": { + "address": { + "label": "Address", + "required": "Member address is required" + }, + "memberName": { + "label": "Member name", + "required": "Member name is required" + } + } + }, + "edit": { + "title": "Edit member", + "form": { + "memberName": { + "label": "Member name", + "required": "Member name is required" + } + } + } + }, + "expenses": { + "add": { + "title": "Add a new expense", + "form": { + "name": { + "label": "Name", + "required": "Expense name is required" + }, + "amount": { + "label": "Amount", + "required": "Expense amount is required" + } + } + } + }, "tabs": { "members": "Members", "expenses": "Operations", @@ -92,6 +157,7 @@ "common": { "submit": "Submit", "cancel": "Cancel", + "confirm": "Confirm", "goToHome": "Go to home" } } \ No newline at end of file diff --git a/packages/fronts/solidr-front-next/src/app/sessions/[sessionId]/components/SessionAddMemberDialog.tsx b/packages/fronts/solidr-front-next/src/app/sessions/[sessionId]/components/SessionAddMemberDialog.tsx new file mode 100644 index 0000000..e93039b --- /dev/null +++ b/packages/fronts/solidr-front-next/src/app/sessions/[sessionId]/components/SessionAddMemberDialog.tsx @@ -0,0 +1,104 @@ +import React, { Suspense, useState } from 'react'; +import { useForm } from 'react-hook-form-mui'; +import { DialogTitle } from '@headlessui/react'; +import { useRecoilValue } from 'recoil'; +import { useAnchorWallet } from '@solana/wallet-adapter-react'; +import { Wallet } from '@coral-xyz/anchor'; +import { sessionCurrentState } from '@/store/sessions'; +import { PublicKey } from '@solana/web3.js'; +import { useTranslations } from 'next-intl'; +import Dialog from '@/components/Dialog'; +import { useSolidrClient } from '@/providers/solidr/SolidrClientContext'; + +interface IAddMemberDialogProps { + dialogVisible: boolean; + setDialogVisible: React.Dispatch>; +} + +interface IRegisterMemberParams { + address: string; + memberName: string; +} + +export default ({ dialogVisible, setDialogVisible }: IAddMemberDialogProps) => { + + const t = useTranslations(); + + const anchorWallet = useAnchorWallet() as Wallet; + const solidrClient = useSolidrClient(); + const { session } = useRecoilValue(sessionCurrentState); + + const [formData, setFormData] = useState>({}); + + const { register, handleSubmit, formState: { errors, isSubmitting } } = useForm({ + defaultValues: formData, + }); + + const onSubmit = async (data: Partial) => { + if (!solidrClient || !session || !data.address || !data.memberName) return; + setFormData(data); + await solidrClient.addSessionMember(anchorWallet, session.sessionId, new PublicKey(data.address), data.memberName); + setDialogVisible(false); + }; + + if (!anchorWallet || !solidrClient || !session) return ; + + return ( + setDialogVisible(false)}> +
+
+ + {t('session.members.add.title')} + +
+
+
+
+
+
+ + + {errors.address && {t('session.members.add.form.address.required')}} +
+ +
+ + + {errors.memberName && {t('session.member.add.form.memberName.required')}} +
+ +
+ + +
+
+
+
+ ); +}; diff --git a/packages/fronts/solidr-front-next/src/app/sessions/[sessionId]/components/SessionBalance.tsx b/packages/fronts/solidr-front-next/src/app/sessions/[sessionId]/components/SessionBalance.tsx new file mode 100644 index 0000000..8bc1077 --- /dev/null +++ b/packages/fronts/solidr-front-next/src/app/sessions/[sessionId]/components/SessionBalance.tsx @@ -0,0 +1,115 @@ +"use client"; + +import { useRecoilValue } from 'recoil'; +import { sessionCurrentState } from '@/store/sessions'; +import { Bar } from 'react-chartjs-2'; +import { BarElement, CategoryScale, Chart as ChartJS, LinearScale, Tooltip as ChartTooltip, ChartOptions } from 'chart.js/auto'; +import { hexToRgba, stringToColor } from '@/services/colors'; +import { useTheme } from 'next-themes'; + +ChartJS.register(BarElement, CategoryScale, LinearScale, ChartTooltip); + +export default () => { + const { theme } = useTheme(); + const sessionCurrent = useRecoilValue(sessionCurrentState); + + const sortedBalances = Object.values(sessionCurrent.balances).sort((a, b) => { + // Sort in descending order, with positive balances first, then negative balances + if (a.balance >= 0 && b.balance >= 0) { + return b.balance - a.balance; + } else if (a.balance < 0 && b.balance < 0) { + return a.balance - b.balance; + } else { + return b.balance >= 0 ? -1 : 1; + } + }); + + return sessionCurrent.members[balance.owner.toString()].name), + datasets: [ + { + data: sortedBalances.map((balance) => balance.balance), + backgroundColor: sortedBalances.map((balance) => hexToRgba(stringToColor(balance.owner.toString()), 1).toString()), + borderColor: sortedBalances.map((balance) => stringToColor(balance.owner.toString())), + hoverBackgroundColor: sortedBalances.map((balance) => hexToRgba(stringToColor(balance.owner.toString()), 0.5).toString()), + barPercentage: 0.9, + borderWidth: 1, + }, + ], + }} + options={{ + plugins: { + title: { + display: false, + text: 'balance of costs', + position: 'bottom' as const, + }, + legend: { + display: false, + }, + customLabels: { + font: { + size: 12, + weight: 'bold', + }, + }, + } as any, + scales: { + x: { + display: false, // Hide x-axis completely + }, + y: { + display: true, // Hide y-axis labels and ticks + beginAtZero: true, + grid: { + drawBorder: false, + color: (context: any) => { + if (context.tick.value === 0) { + return theme === 'light' ? 'rgba(0, 0, 0, 0.1)' : 'rgba(255, 255, 255, 0.1)'; // Color of the zero line + } + return 'rgba(0, 0, 0, 0)'; // Transparent color for other grid lines + }, + } as any, + ticks: { + display: false, + }, + border: { + display: false, + }, + }, + }, + layout: { + padding: { + top: 40, + right: 40, + bottom: 40, + left: 40, + }, + }, + maintainAspectRatio: false, // Set this to false to allow the chart to resize freely + responsive: true, // Set this to true to make the chart responsive + }} + plugins={[{ + id: 'customLabels', + afterDatasetsDraw(chart, args, pluginOptions) { + const { ctx, data, scales } = chart; + + ctx.save(); + ctx.font = `${pluginOptions?.font.weight} ${pluginOptions?.font.size}px sans-serif`; + ctx.textAlign = 'center'; + + data.datasets[0].data.forEach((value, index) => { + const x = scales.x.getPixelForValue(index); + const y = scales.y.getPixelForValue(Number(value)); + + ctx.fillStyle = Number(value) >= 0 ? '#1dc17d' : '#e36f6f'; + const yPos = Number(value) >= 0 ? y - 10 : y + 20; + ctx.fillText(`${Number(value).toFixed(2)}$`, x, yPos); + }); + + ctx.restore(); + }, + }]} + />; +}; diff --git a/packages/fronts/solidr-front-next/src/app/sessions/[sessionId]/components/SessionEditMemberDialog.tsx b/packages/fronts/solidr-front-next/src/app/sessions/[sessionId]/components/SessionEditMemberDialog.tsx new file mode 100644 index 0000000..e07e15f --- /dev/null +++ b/packages/fronts/solidr-front-next/src/app/sessions/[sessionId]/components/SessionEditMemberDialog.tsx @@ -0,0 +1,91 @@ +import React, { Suspense, useState } from 'react'; +import { useForm } from 'react-hook-form-mui'; +import { DialogTitle } from '@headlessui/react'; +import { useAnchorWallet } from '@solana/wallet-adapter-react'; +import { Wallet } from '@coral-xyz/anchor'; +import { useTranslations } from 'next-intl'; +import Dialog from '@/components/Dialog'; +import { useSolidrClient } from '@/providers/solidr/SolidrClientContext'; +import { Session, SessionMember } from '@solidr'; +import { useRecoilValue } from 'recoil'; +import { sessionCurrentState } from '@/store/sessions'; + +interface IEditMemberDialogProps { + dialogVisible: boolean; + setDialogVisible: React.Dispatch>; + member: SessionMember; +} + +interface IEditMemberParams { + memberName: string; +} + +export default ({ dialogVisible, setDialogVisible, member }: IEditMemberDialogProps) => { + + const t = useTranslations(); + + const anchorWallet = useAnchorWallet() as Wallet; + const solidrClient = useSolidrClient(); + + const [formData, setFormData] = useState>({}); + const { session } = useRecoilValue(sessionCurrentState); + + const { register, handleSubmit, formState: { errors, isSubmitting } } = useForm({ + defaultValues: formData, + }); + + const onSubmit = async (data: Partial) => { + if (!solidrClient || !session || !data.memberName) return; + setFormData(data); + await solidrClient.updateSessionMember(anchorWallet, session.sessionId, member.addr, data.memberName); + setDialogVisible(false); + }; + + if (!anchorWallet || !solidrClient || !session) return ; + + return ( + setDialogVisible(false)}> +
+
+ + {t('session.members.edit.title')} + +
+
+
+
+
+
+ + + {errors.memberName && {t('session.member.edit.form.memberName.required')}} +
+ +
+ + +
+
+
+
+ ); +}; diff --git a/packages/fronts/solidr-front-next/src/app/sessions/[sessionId]/components/SessionExpenseSummary.tsx b/packages/fronts/solidr-front-next/src/app/sessions/[sessionId]/components/SessionExpenseSummary.tsx new file mode 100644 index 0000000..3c81fa1 --- /dev/null +++ b/packages/fronts/solidr-front-next/src/app/sessions/[sessionId]/components/SessionExpenseSummary.tsx @@ -0,0 +1,44 @@ + + +import { useTranslations } from 'next-intl'; + +interface Props { + myTotalCost: number; + totalExpenses: number; + totalRefunds: number; +} +export default ({ myTotalCost, totalExpenses, totalRefunds }: Props) => { + const t = useTranslations(); + + return
+ {/* Total Cost */} +
+
+ {t('session.summary.totalCost')} +
+

+ {myTotalCost}$ +

+
+ + {/* Total Expenses */} +
+
+ {t('session.summary.totalExpenses')} +
+

+ {totalExpenses}$ +

+
+ + {/* Total Refunds (Visible only on lg screens and above) */} +
+
+ {t('session.summary.totalRefunds')} +
+

+ {totalRefunds}$ +

+
+
+} \ No newline at end of file diff --git a/packages/fronts/solidr-front-next/src/app/sessions/[sessionId]/components/SessionInfo.tsx b/packages/fronts/solidr-front-next/src/app/sessions/[sessionId]/components/SessionInfo.tsx index 77df1d5..4c16080 100644 --- a/packages/fronts/solidr-front-next/src/app/sessions/[sessionId]/components/SessionInfo.tsx +++ b/packages/fronts/solidr-front-next/src/app/sessions/[sessionId]/components/SessionInfo.tsx @@ -32,13 +32,13 @@ export default ({ session }: { session: Session }) => { title: t('session.action.edit.menu.title'), description: t('session.action.edit.menu.description'), onClick: () => router.push(`/sessions/${session.sessionId}/edit`), - icon: , + icon: , }); _menuItems.push({ title: t('session.action.close.menu.title'), description: t('session.action.close.menu.description'), onClick: () => setShowConfirmCloseDialog(true), - icon: , + icon: , }); } if (session.status == SessionStatus.Closed) { @@ -46,7 +46,7 @@ export default ({ session }: { session: Session }) => { title: t('session.action.delete.menu.title'), description: t('session.action.delete.menu.description'), onClick: () => setShowConfirmDeleteDialog(true), - icon: , + icon: , }); } } diff --git a/packages/fronts/solidr-front-next/src/app/sessions/[sessionId]/components/SessionInviteMemberDialog.tsx b/packages/fronts/solidr-front-next/src/app/sessions/[sessionId]/components/SessionInviteMemberDialog.tsx new file mode 100644 index 0000000..a89f348 --- /dev/null +++ b/packages/fronts/solidr-front-next/src/app/sessions/[sessionId]/components/SessionInviteMemberDialog.tsx @@ -0,0 +1,113 @@ +import React, { Suspense, useEffect, useState } from 'react'; +import { useRecoilValue } from 'recoil'; +import { useAnchorWallet, useLocalStorage } from '@solana/wallet-adapter-react'; +import { BN, Wallet } from '@coral-xyz/anchor'; +import { sessionCurrentState } from '@/store/sessions'; +import QRCode from 'qrcode.react'; +import { useSolidrClient } from '@/providers/solidr/SolidrClientContext'; +import Dialog from '@/components/Dialog'; +import { ClipboardIcon } from '@heroicons/react/24/solid'; +import { useTranslations } from 'next-intl'; +import { DialogTitle } from '@headlessui/react'; + +interface IInviteMemberDialogProps { + dialogVisible: boolean; + setDialogVisible: React.Dispatch>; +} + +export default function InviteMemberDialog({ dialogVisible, setDialogVisible }: IInviteMemberDialogProps) { + const t = useTranslations(); + const anchorWallet = useAnchorWallet() as Wallet; + const solidrClient = useSolidrClient(); + const sessionCurrent = useRecoilValue(sessionCurrentState); + const [invitationToken, setInvitationToken] = useLocalStorage(`solidr.sessions.${sessionCurrent?.session?.sessionId}`, ""); + const [url, setUrl] = useState(''); + + const generateUrl = () => { + if (!solidrClient || !sessionCurrent?.session) return; + + const invitationUrl = `${window.location.origin}/sessions/${sessionCurrent.session.sessionId}/join/${invitationToken}`; + setUrl(invitationUrl); + }; + + const onSubmit = async () => { + if (!solidrClient || !sessionCurrent.session) return; + const { data: { token } } = await solidrClient.generateSessionLink(anchorWallet, new BN(sessionCurrent.session.sessionId)) + setInvitationToken(token); + generateUrl(); + }; + + useEffect(() => { + if (invitationToken) { + generateUrl(); + } + }, [invitationToken]); + + if (!anchorWallet || !solidrClient || !sessionCurrent?.session) return ; + + return ( + setDialogVisible(false)}> +
+
+ + {t('session.share.title')} + +
+
+
+
+
+
+ {url && invitationToken && ( + <> +
+ +
+ + + +
+
+
+ +
+ + )} +
+
+
+ +
+ + + +
+
+ ); +} diff --git a/packages/fronts/solidr-front-next/src/app/sessions/[sessionId]/components/SessionMemberList.tsx b/packages/fronts/solidr-front-next/src/app/sessions/[sessionId]/components/SessionMemberList.tsx new file mode 100644 index 0000000..9f3b5a9 --- /dev/null +++ b/packages/fronts/solidr-front-next/src/app/sessions/[sessionId]/components/SessionMemberList.tsx @@ -0,0 +1,94 @@ + + +import _ from 'lodash'; +import { sessionCurrentState } from '@/store/sessions'; +import { PaperAirplaneIcon, PencilIcon, PlusCircleIcon } from '@heroicons/react/24/solid'; +import { useAnchorWallet } from '@solana/wallet-adapter-react'; +import { SessionMember, SessionStatus } from '@solidr'; +import { useTranslations } from 'next-intl'; +import { useState } from 'react'; +import { useRecoilValue } from 'recoil'; +import SessionAddMemberDialog from './SessionAddMemberDialog'; +import SessionInviteMemberDialog from './SessionInviteMemberDialog'; +import SectionTitleWrapper from '@/components/SectionTitleWrapper'; +import AddressAvatar from '@/components/AddressAvatar'; +import { Wallet } from '@coral-xyz/anchor'; +import SessionBalance from './SessionBalance'; +import SessionEditMemberDialog from './SessionEditMemberDialog'; + +export default () => { + const t = useTranslations(); + + const wallet = useAnchorWallet() as Wallet; + const [addMemberDialogVisible, setAddMemberDialogVisible] = useState(false); + const [inviteMemberDialogVisible, setInviteMemberDialogVisible] = useState(false); + const [editMemberDialogVisible, setEditMemberDialogVisible] = useState(false); + const [selectedMember, setSelectedMember] = useState(undefined); + + const sessionCurrent = useRecoilValue(sessionCurrentState); + + const handleMemberClick = (member: SessionMember) => { + if (wallet.publicKey?.toString() != member.addr.toString() && !sessionCurrent.isAdmin) { + return; + } + + setSelectedMember(member); + setEditMemberDialogVisible(true); + }; + + return
+ +

+ {t('session.members.list.title')} +

+
+ + +
+
+ +
+
+
    + {_.map(sessionCurrent.members, (member, address) => ( +
  • +
    +
    +
    + +
    +
    + {member.name} +
    + {(wallet.publicKey.toString() === member.addr.toString() || sessionCurrent.isAdmin) && sessionCurrent.session?.status === SessionStatus.Opened && ( + + )} +
  • + ))} +
+
+
+ +
+
+ + + + {selectedMember && } +
+} \ No newline at end of file diff --git a/packages/fronts/solidr-front-next/src/app/sessions/[sessionId]/join/[token]/page.tsx b/packages/fronts/solidr-front-next/src/app/sessions/[sessionId]/join/[token]/page.tsx new file mode 100644 index 0000000..41f8c9f --- /dev/null +++ b/packages/fronts/solidr-front-next/src/app/sessions/[sessionId]/join/[token]/page.tsx @@ -0,0 +1,111 @@ +"use client"; + +import React, { Suspense, useEffect, useState } from 'react'; +import { useForm } from 'react-hook-form'; +import _ from 'lodash'; +import { useAnchorWallet } from '@solana/wallet-adapter-react'; +import { BN, Wallet } from '@coral-xyz/anchor'; +import { useSolidrClient } from '@/providers/solidr/SolidrClientContext'; +import { useTranslations } from 'next-intl'; +import PageTitleWrapper from '@/components/PageTitleWrapper'; +import Link from 'next/link'; +import { useParams, useRouter } from 'next/navigation'; +import { Session } from '@solidr'; +import SessionNotFound from '../../components/SessionNotFound'; + +interface IEditSessionParams { + memberName: string; +} + +export default () => { + + const t = useTranslations(); + const { sessionId, token } = useParams(); + const router = useRouter(); + const anchorWallet = useAnchorWallet() as Wallet; + const solidrClient = useSolidrClient(); + + const [session, setSession] = useState(); + const [sessionNotFound, setSessionNotFound] = useState(false) + + const [formData, setFormData] = useState>({}); + + const { register, handleSubmit, formState: { errors, isSubmitting } } = useForm({ + defaultValues: formData, + }); + + const onSubmit = async (data: any) => { + if (!solidrClient || !session || !data.memberName) return; + setFormData(data); + await solidrClient.joinSessionAsMember(anchorWallet, session.sessionId, data.memberName, token as string); + router.push(`/sessions/${sessionId}`) + }; + + useEffect(() => { + if (solidrClient == null || sessionId == null) return; + + if (!session || session.sessionId.toString() !== sessionId) { + const sessionAccountAddress = solidrClient.findSessionAccountAddress(new BN(sessionId)); + + solidrClient.getSession(sessionAccountAddress).then((session) => { + if (!session) { + setSessionNotFound(true); + } else { + setSession(session); + } + }); + } + }); + + if (!anchorWallet || !solidrClient || !session) return ; + + if (sessionNotFound) { + return + } + + return ( +
+
+ +
+

+ {t('session.join.title', { name: session.name })} +

+
+
+ +
+
+ + + {errors.memberName && {t('session.join.form.memberName.required')}} +
+ +
+ + + + +
+
+
+
+ + ); +}; diff --git a/packages/fronts/solidr-front-next/src/app/sessions/[sessionId]/page.tsx b/packages/fronts/solidr-front-next/src/app/sessions/[sessionId]/page.tsx index 3d2e6c2..3f0453f 100644 --- a/packages/fronts/solidr-front-next/src/app/sessions/[sessionId]/page.tsx +++ b/packages/fronts/solidr-front-next/src/app/sessions/[sessionId]/page.tsx @@ -3,7 +3,7 @@ import React, { Suspense, useEffect, useState } from 'react'; import { useAnchorWallet } from '@solana/wallet-adapter-react'; import { BN, Wallet } from '@coral-xyz/anchor'; -import { SessionMember, SessionStatus } from '@solidr'; +import { Session, SessionMember, SessionStatus } from '@solidr'; import { SessionCurrentState, defaultSessionState, sessionCurrentState } from '@/store/sessions'; import { useTranslations } from 'next-intl'; import { useSolidrClient } from '@/providers/solidr/SolidrClientContext'; @@ -15,12 +15,16 @@ import SessionAccessDenied from './components/SessionAccessDenied'; import SessionNotFound from './components/SessionNotFound'; import SessionInfo from './components/SessionInfo'; import { TabPanel, TabPanelButton } from '@/components/TabPanel.tsx'; +import SessionExpenseSummary from './components/SessionExpenseSummary'; +import SessionMemberList from './components/SessionMemberList'; +import { PublicKey } from '@solana/web3.js'; export default () => { const t = useTranslations(); const [value, setValue] = useState(0); const [sessionNotFound, setSessionNotFound] = useState(false) + const [session, setSession] = useState() const handleChange = (index: number) => { setValue(index); @@ -34,7 +38,6 @@ export default () => { const [sessionCurrent, setSessionCurrent] = useRecoilState(sessionCurrentState); const solidrClient = useSolidrClient(); const anchorWallet = useAnchorWallet() as Wallet; - // const params = useHashParams(); const updateSessionClosed = () => { setSessionCurrent((sessionState) => { @@ -213,9 +216,6 @@ export default () => { } if (!_.keys(sessionCurrent.members).includes(anchorWallet.publicKey.toString())) { - // if (params.token) { - // return ; - // } return ; } @@ -227,7 +227,11 @@ export default () => {
- {/* */} +
@@ -239,7 +243,7 @@ export default () => {
- {/* */} +
diff --git a/packages/fronts/solidr-front-next/src/app/sessions/create/page.tsx b/packages/fronts/solidr-front-next/src/app/sessions/create/page.tsx index 8110a60..72a517b 100644 --- a/packages/fronts/solidr-front-next/src/app/sessions/create/page.tsx +++ b/packages/fronts/solidr-front-next/src/app/sessions/create/page.tsx @@ -28,7 +28,7 @@ export default () => { defaultValues: formData, }); - const onSubmit = async (data: any) => { + const onSubmit = async (data: Partial) => { if (!solidrClient || !data.name || !data.description || !data.memberName) return; setFormData(data); const { data: { sessionId } } = await solidrClient.openSession(anchorWallet, data.name, data.description, data.memberName); @@ -102,8 +102,8 @@ export default () => {
-
-
+ + ); }; diff --git a/packages/fronts/solidr-front-next/src/components/AddressAvatar/index.tsx b/packages/fronts/solidr-front-next/src/components/AddressAvatar/index.tsx new file mode 100644 index 0000000..532cc2e --- /dev/null +++ b/packages/fronts/solidr-front-next/src/components/AddressAvatar/index.tsx @@ -0,0 +1,37 @@ +import { stringToColor } from '@/services/colors'; + +function stringAvatar(name: string, size?: number, marginLeft?: string) { + const backgroundColor = stringToColor(name); + const fontSize = size ? `${size / 2}px` : '20px'; + const widthHeight = size ? `${size}px` : '40px'; + + return { + style: { + backgroundColor, + fontSize, + width: widthHeight, + height: widthHeight, + marginLeft: marginLeft || '0', + }, + children: name.substring(0, 2).toUpperCase(), + }; +} + +interface IProps { + address: string; + size?: number; + marginLeft?: string; +} + +export default function AddressAvatar({ address, size, marginLeft }: IProps) { + const avatarProps = stringAvatar(address, size, marginLeft); + + return ( +
+ {avatarProps.children} +
+ ); +} diff --git a/packages/fronts/solidr-front-next/src/components/ConfirmationDialog/index.tsx b/packages/fronts/solidr-front-next/src/components/ConfirmationDialog/index.tsx index 74e0a2d..42ad46b 100644 --- a/packages/fronts/solidr-front-next/src/components/ConfirmationDialog/index.tsx +++ b/packages/fronts/solidr-front-next/src/components/ConfirmationDialog/index.tsx @@ -1,7 +1,10 @@ -import { Fragment } from 'react'; -import { Dialog, Transition, TransitionChild, DialogTitle, DialogPanel } from '@headlessui/react'; +import { DialogTitle } from '@headlessui/react'; import { ExclamationTriangleIcon } from '@heroicons/react/24/solid'; +import TransitionDialog from '../Dialog'; +import { useTranslations } from 'next-intl'; +import Dialog from '../Dialog'; + interface ConfirmationDialogProps { isOpen: boolean; onClose: () => void; @@ -17,70 +20,42 @@ const ConfirmationDialog = ({ title, description, }: ConfirmationDialogProps) => { - return ( - - - -
- + const t = useTranslations(); -
-
- - -
-
-
-
- - {title} - -
-

- {description} -

-
-
-
-
- - -
-
-
+ return ( + +
+
+
+
+ + {title} + +
+

+ {description} +

-
- +
+
+ + +
+
); }; diff --git a/packages/fronts/solidr-front-next/src/components/Dialog/index.tsx b/packages/fronts/solidr-front-next/src/components/Dialog/index.tsx new file mode 100644 index 0000000..78d08de --- /dev/null +++ b/packages/fronts/solidr-front-next/src/components/Dialog/index.tsx @@ -0,0 +1,43 @@ +import { Fragment } from 'react'; +import { Dialog, Transition, TransitionChild, DialogTitle, DialogPanel } from '@headlessui/react'; + +const TransitionDialog = ({ isOpen, onClose, children }: { isOpen: boolean; onClose: () => void; children: React.ReactNode }) => { + return ( + + + +
+ + +
+
+ + + {children} + + +
+
+
+
+ + ); +}; + +export default TransitionDialog; diff --git a/packages/fronts/solidr-front-next/src/components/PageTitleWrapper/index.tsx b/packages/fronts/solidr-front-next/src/components/PageTitleWrapper/index.tsx index 4ca43a4..9c9ed85 100644 --- a/packages/fronts/solidr-front-next/src/components/PageTitleWrapper/index.tsx +++ b/packages/fronts/solidr-front-next/src/components/PageTitleWrapper/index.tsx @@ -3,7 +3,7 @@ import React from 'react'; export default ({ children }: { children?: React.ReactNode }) => { return ( <> -
+
{children} diff --git a/packages/fronts/solidr-front-next/src/components/SectionTitleWrapper/index.tsx b/packages/fronts/solidr-front-next/src/components/SectionTitleWrapper/index.tsx new file mode 100644 index 0000000..3cd6e5a --- /dev/null +++ b/packages/fronts/solidr-front-next/src/components/SectionTitleWrapper/index.tsx @@ -0,0 +1,13 @@ +import React from 'react'; + +export default ({ children }: { children?: React.ReactNode }) => { + return ( +
+
+
+ {children} +
+
+
+ ); +}; diff --git a/packages/fronts/solidr-front-next/src/services/colors.ts b/packages/fronts/solidr-front-next/src/services/colors.ts new file mode 100644 index 0000000..df79d5a --- /dev/null +++ b/packages/fronts/solidr-front-next/src/services/colors.ts @@ -0,0 +1,30 @@ +export function stringToColor(string: string): string { + let hash = 0; + for (let i = 0; i < string.length; i += 1) { + hash = string.charCodeAt(i) + ((hash << 5) - hash); + } + + // Generate a more contrasting color + const r = (hash & 0xff) | 0x80; + const g = ((hash >> 8) & 0xff) | 0x80; + const b = ((hash >> 16) & 0xff) | 0x80; + + return `#${(r.toString(16) + g.toString(16) + b.toString(16)).padStart(6, '0')}`; +} + +export function hexToRgb(hex: string) { + if (hex.charAt(0) === '#') { + hex = hex.slice(1); + } + + let r = parseInt(hex.substring(0, 2), 16); + let g = parseInt(hex.substring(2, 4), 16); + let b = parseInt(hex.substring(4, 6), 16); + + return { r: r, g: g, b: b }; +} + +export function hexToRgba(hex: string, alpha: number) { + let rgb = hexToRgb(hex); + return `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, ${alpha})`; +}