Skip to content

Commit

Permalink
feature(website): update account dashboard
Browse files Browse the repository at this point in the history
  • Loading branch information
mkue committed Nov 11, 2023
1 parent d2a190e commit f07fd05
Show file tree
Hide file tree
Showing 48 changed files with 673 additions and 258 deletions.
2 changes: 1 addition & 1 deletion seed/auth_export/accounts.json

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions seed/firebase-export-metadata.json
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
{
"version": "12.4.4",
"version": "12.7.0",
"firestore": {
"version": "1.18.1",
"version": "1.18.2",
"path": "firestore_export",
"metadata_file": "firestore_export/firestore_export.overall_export_metadata"
},
"auth": {
"version": "12.4.4",
"version": "12.7.0",
"path": "auth_export"
},
"storage": {
"version": "12.4.4",
"version": "12.7.0",
"path": "storage_export"
}
}
Binary file not shown.
Binary file modified seed/firestore_export/all_namespaces/all_kinds/output-0
Binary file not shown.
Binary file modified seed/firestore_export/firestore_export.overall_export_metadata
Binary file not shown.
40 changes: 36 additions & 4 deletions shared/locales/en/website-me.json
Original file line number Diff line number Diff line change
@@ -1,12 +1,44 @@
{
"tabs": {
"contact-details": "Contact Info",
"contributions": "Contributions"
"sections": {
"account": {
"title": "My Account",
"personal-info": "Personal Info",
"security": "Security"
},
"contributions": {
"title": "My Contributions",
"payments": "Payments",
"subscriptions": "Subscriptions"
}
},
"contributions": {
"amount": "Amount",
"date": "Date",
"source": "Source",
"total": "Total",
"amount-currency": "{{ amount, currency }}",
"date": "Date"
"sources": {
"benevity": "Benevity",
"cash": "Cash",
"stripe": "Stripe",
"wire-transfer": "Wire Transfer"
}
},
"subscriptions": {
"amount": "Amount",
"date": "Date",
"source": "Source",
"total": "Total",
"amount-currency": "{{ amount, currency }}",
"interval": "Interval",
"interval-1": "Monthly",
"interval-3": "Quarterly",
"interval-12": "Annually",
"status": {
"active": "Active",
"canceled": "Canceled",
"paused": "Paused"
}
},
"login": {
"title": "Sign in to your account",
Expand Down
22 changes: 10 additions & 12 deletions shared/src/firebase/client/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,16 @@ interface InitializeFirebaseClientProps {
functionsEmulatorPort?: number;
}

export const initializeFirebaseClient = (
{
firebaseConfig,
authEmulatorUrl,
firestoreEmulatorHost,
firestoreEmulatorPort,
storageEmulatorHost,
storageEmulatorPort,
functionsEmulatorHost,
functionsEmulatorPort,
}: InitializeFirebaseClientProps,
) => {
export const initializeFirebaseClient = ({
firebaseConfig,
authEmulatorUrl,
firestoreEmulatorHost,
firestoreEmulatorPort,
storageEmulatorHost,
storageEmulatorPort,
functionsEmulatorHost,
functionsEmulatorPort,
}: InitializeFirebaseClientProps) => {
const app = getOrInitializeFirebaseClientApp(firebaseConfig);
const auth = getAuth(app);
const functions = getFunctions(app, DEFAULT_REGION);
Expand Down
15 changes: 11 additions & 4 deletions shared/src/utils/date.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,17 @@ export function getMonthIDs(date: Date, last_n: number) {
return months;
}

export function toDateTime(timestamp: Timestamp | Date, timezone: string = 'utc') {
return timestamp instanceof Date
? DateTime.fromJSDate(timestamp, { zone: timezone })
: DateTime.fromMillis(timestamp.toMillis(), { zone: timezone });
export function toDateTime(timestamp: Timestamp | Date | number, timezone: string = 'utc') {
if (timestamp instanceof Date) {
timestamp = timestamp as Date;
return DateTime.fromJSDate(timestamp, { zone: timezone });
} else if (Number.isInteger(timestamp)) {
timestamp = timestamp as number;
return DateTime.fromMillis(timestamp as number, { zone: timezone });
} else {
timestamp = timestamp as Timestamp;
return DateTime.fromMillis(timestamp.toMillis(), { zone: timezone });
}
}

export function toDate(dateTime: DateTime) {
Expand Down
12 changes: 9 additions & 3 deletions shared/src/utils/messaging/email.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,15 @@ interface SendEmailProps {
password: string;
}

export const sendEmail = async (
{ from = '[email protected]', to, subject, content, attachments = [], user, password }: SendEmailProps,
) => {
export const sendEmail = async ({
from = '[email protected]',
to,
subject,
content,
attachments = [],
user,
password,
}: SendEmailProps) => {
let transporter: Transporter;
if (!user) {
const testAccount = await nodemailer.createTestAccount();
Expand Down
9 changes: 6 additions & 3 deletions shared/src/utils/messaging/whatsapp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,12 @@ export interface SendWhatsappProps {
templateProps: RenderTemplateProps;
}

export const sendWhatsapp = async (
{ to, from, twilioConfig, templateProps }: SendWhatsappProps,
): Promise<MessageInstance> => {
export const sendWhatsapp = async ({
to,
from,
twilioConfig,
templateProps,
}: SendWhatsappProps): Promise<MessageInstance> => {
const client = new Twilio(twilioConfig.sid, twilioConfig.token);
const body = await renderTemplate(templateProps);
return client.messages.create({ body: body, from: `whatsapp:${from}`, to: `whatsapp:${to}` });
Expand Down
9 changes: 6 additions & 3 deletions shared/src/utils/templates.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,12 @@ const partials = [
];
partials.forEach((partial) => Handlebars.registerPartial(partial.name, readHbs(partial.path)));

export const renderTemplate = async (
{ language, translationNamespace = [], hbsTemplatePath, context }: RenderTemplateProps,
) => {
export const renderTemplate = async ({
language,
translationNamespace = [],
hbsTemplatePath,
context,
}: RenderTemplateProps) => {
const i18n = i18next.createInstance();
await i18n
.use(
Expand Down
8 changes: 5 additions & 3 deletions ui/CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,11 @@ UI elements:
**Example**:

```tsx
export const SoExampleComponent = (
{ children, exampleProperty, ...props }: SoExampleComponentProps,
) => {
export const SoExampleComponent = ({
children,
exampleProperty,
...props
}: SoExampleComponentProps) => {
return (
<p example-property={exampleProperty} {...props}>
{children}
Expand Down
2 changes: 1 addition & 1 deletion ui/src/components/button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ const buttonVariants = cva(
size: {
default: 'h-10 px-4 py-2',
sm: 'h-9 rounded-md px-3',
lg: 'h-16 rounded-md px-8',
lg: 'h-16 rounded-md px-8 font-semibold',
icon: 'h-10 w-10',
},
},
Expand Down
11 changes: 8 additions & 3 deletions ui/src/components/carousel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,14 @@ type CarouselProps = {
showControls?: boolean;
} & React.HTMLAttributes<HTMLDivElement>;

export const Carousel = (
{ children, className, options = {}, showDots = false, showControls = false, ...props }: CarouselProps,
) => {
export const Carousel = ({
children,
className,
options = {},
showDots = false,
showControls = false,
...props
}: CarouselProps) => {
const [emblaRef, emblaApi] = useEmblaCarousel(
options,
options?.autoPlay?.enabled ? [Autoplay(options.autoPlay)] : [],
Expand Down
6 changes: 3 additions & 3 deletions ui/src/components/form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@ const FormFieldContext = React.createContext<FormFieldContextValue>({} as FormFi
const FormField = <
TFieldValues extends FieldValues = FieldValues,
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
>(
{ ...props }: ControllerProps<TFieldValues, TName>,
) => {
>({
...props
}: ControllerProps<TFieldValues, TName>) => {
return (
<FormFieldContext.Provider value={{ name: props.name }}>
<Controller {...props} />
Expand Down
22 changes: 10 additions & 12 deletions ui/src/components/typography/typography.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -66,18 +66,16 @@ export type TypographyProps<C extends ElementType> = {
lineHeight?: LineHeight;
} & ComponentPropsWithoutRef<C>;

export function Typography<C extends ElementType = 'p'>(
{
as,
size = 'md',
weight = 'normal',
color = 'foreground',
lineHeight = 'normal',
className,
children,
...props
}: TypographyProps<C>,
) {
export function Typography<C extends ElementType = 'p'>({
as,
size = 'md',
weight = 'normal',
color = 'foreground',
lineHeight = 'normal',
className,
children,
...props
}: TypographyProps<C>) {
const Component = as || 'p';
return (
<Component
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,8 @@ export default function Section1Form({ translations }: Section1InputProps) {
/>
<CurrencySelector className="h-16 sm:flex-1" currencies={websiteCurrencies} fontSize="lg" />
</div>
<Button size="lg" type="submit" variant="default">
<Typography size="lg" weight="semibold" color="primary-foreground">
{translations.submit}
</Typography>
<Button size="lg" type="submit" variant="default" className="text-lg">
{translations.submit}
</Button>
</form>
</Form>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
'use client';

import { DefaultPageProps } from '@/app/[lang]/[region]';
import { CreateSubscriptionData } from '@/app/api/stripe/checkout/new/route';
import { CreateSubscriptionData } from '@/app/api/stripe/checkout/new-payment/route';
import { CheckCircleIcon } from '@heroicons/react/24/outline';
import { zodResolver } from '@hookform/resolvers/zod';
import {
Expand All @@ -19,6 +19,7 @@ import {
import classNames from 'classnames';
import { useRouter } from 'next/navigation';
import { UseFormReturn, useForm } from 'react-hook-form';
import { useUser } from 'reactfire';
import Stripe from 'stripe';
import * as z from 'zod';

Expand Down Expand Up @@ -67,6 +68,7 @@ function RadioGroupFormItem({ active, title, value, form, description }: RadioGr

export default function Page({ params, searchParams }: DefaultPageProps) {
const router = useRouter();
const { data: authUser } = useUser();

const formSchema = z.object({
amount: z.coerce.number(),
Expand All @@ -80,12 +82,15 @@ export default function Page({ params, searchParams }: DefaultPageProps) {
});

const onSubmit = async (values: FormSchema) => {
const authToken = await authUser?.getIdToken(true);
const data: CreateSubscriptionData = {
amount: values.amount * 100, // The amount is in cents, so we need to multiply by 100 to get the correct amount.
intervalCount: Number(values.intervalCount),
successUrl: `${window.location.origin}/${params.lang}/${params.region}/donate/success?stripeCheckoutSessionId={CHECKOUT_SESSION_ID}`,
recurring: true,
firebaseAuthToken: authToken,
};
const response = await fetch('/api/stripe/checkout/new', {
const response = await fetch('/api/stripe/checkout/new-payment', {
method: 'POST',
body: JSON.stringify(data),
});
Expand Down
32 changes: 18 additions & 14 deletions website/src/app/[lang]/[region]/(website)/login/login-form.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
'use client';

import { DefaultPageProps } from '@/app/[lang]/[region]';
import { DefaultParams } from '@/app/[lang]/[region]';
import { zodResolver } from '@hookform/resolvers/zod';
import { SiGoogle } from '@icons-pack/react-simple-icons';
import { Button, Form, FormControl, FormField, FormItem, FormMessage, Input, Typography } from '@socialincome/ui';
import { FirebaseError } from 'firebase/app';
import { browserSessionPersistence, signInWithEmailAndPassword } from 'firebase/auth';
import { useRouter } from 'next/navigation';
import { useCallback } from 'react';
import { useForm } from 'react-hook-form';
import toast from 'react-hot-toast';
import { useAuth } from 'reactfire';
Expand All @@ -26,9 +27,9 @@ type LoginFormProps = {
unknownUser: string;
wrongPassword: string;
};
} & DefaultPageProps;
} & DefaultParams;

export default function LoginForm({ params, translations }: LoginFormProps) {
export default function LoginForm({ lang, region, translations }: LoginFormProps) {
const router = useRouter();
const auth = useAuth();

Expand All @@ -43,17 +44,20 @@ export default function LoginForm({ params, translations }: LoginFormProps) {
defaultValues: { email: '', password: '' },
});

const onSubmit = async (values: FormSchema) => {
await auth.setPersistence(browserSessionPersistence);
await signInWithEmailAndPassword(auth, values.email, values.password)
.then(() => {
router.push(`/${params.lang}/${params.region}/me`);
})
.catch((error: FirebaseError) => {
error.code === 'auth/wrong-password' && toast.error(translations.wrongPassword);
error.code === 'auth/user-not-found' && toast.error(translations.unknownUser);
});
};
const onSubmit = useCallback(
async (values: FormSchema) => {
await auth.setPersistence(browserSessionPersistence);
await signInWithEmailAndPassword(auth, values.email, values.password)
.then(() => {
router.push(`/${lang}/${region}/me`);
})
.catch((error: FirebaseError) => {
error.code === 'auth/wrong-password' && toast.error(translations.wrongPassword);
error.code === 'auth/user-not-found' && toast.error(translations.unknownUser);
});
},
[auth, lang, region, router, translations.wrongPassword, translations.unknownUser],
);

return (
<div className="mx-auto flex max-w-xl flex-col space-y-8">
Expand Down
7 changes: 4 additions & 3 deletions website/src/app/[lang]/[region]/(website)/login/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@ import { DefaultPageProps } from '@/app/[lang]/[region]';
import LoginForm from '@/app/[lang]/[region]/(website)/login/login-form';
import { Translator } from '@socialincome/shared/src/utils/i18n';

export default async function Page(props: DefaultPageProps) {
const translator = await Translator.getInstance({ language: props.params.lang, namespaces: 'website-me' });
export default async function Page({ params }: DefaultPageProps) {
const translator = await Translator.getInstance({ language: params.lang, namespaces: ['website-me'] });

return (
<LoginForm
lang={params.lang}
region={params.region}
translations={{
title: translator.t('login.title'),
email: translator.t('login.email'),
Expand All @@ -18,7 +20,6 @@ export default async function Page(props: DefaultPageProps) {
unknownUser: translator.t('login.unknown-user'),
wrongPassword: translator.t('login.wrong-password'),
}}
{...props}
/>
);
}
Loading

0 comments on commit f07fd05

Please sign in to comment.