diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 4e2da5d..3d1c061 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -1,35 +1,35 @@ name: Deploy to GitHub Pages on: - push: - branches: - - deploy # branch to deploy from. Force redeploy + push: + branches: + - deploy # branch to deploy from. Force redeploy jobs: - build: - environment: github-pages - runs-on: ubuntu-latest + build: + environment: github-pages + runs-on: ubuntu-latest - steps: - - name: Checkout repository - uses: actions/checkout@v2 + steps: + - name: Checkout repository + uses: actions/checkout@v2 - - name: Set up Node.js - uses: actions/setup-node@v2 - with: - node-version: '18' + - name: Set up Node.js + uses: actions/setup-node@v2 + with: + node-version: '18' - - name: Install dependencies - run: npm install + - name: Install dependencies + run: npm install - - name: Build the project - env: - VITE_SUPABASE_URL: ${{ secrets.VITE_SUPABASE_URL }} - VITE_SUPABASE_ANON_KEY: ${{ secrets.VITE_SUPABASE_ANON_KEY }} - run: npm run build + - name: Build the project + env: + VITE_SUPABASE_URL: ${{ secrets.VITE_SUPABASE_URL }} + VITE_SUPABASE_ANON_KEY: ${{ secrets.VITE_SUPABASE_ANON_KEY }} + run: npm run build - - name: Deploy to GitHub Pages - uses: peaceiris/actions-gh-pages@v3 - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - publish_dir: ./dist + - name: Deploy to GitHub Pages + uses: peaceiris/actions-gh-pages@v3 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + publish_dir: ./dist diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..56abf3f --- /dev/null +++ b/.prettierrc @@ -0,0 +1,6 @@ +{ + "trailingComma": "es5", + "tabWidth": 4, + "singleQuote": true, + "semi": true + } \ No newline at end of file diff --git a/.storybook/main.ts b/.storybook/main.ts index cb9878e..8ad434b 100644 --- a/.storybook/main.ts +++ b/.storybook/main.ts @@ -1,18 +1,18 @@ import type { StorybookConfig } from '@storybook/react-vite'; const config: StorybookConfig = { - // stories: ["../src/**/*.mdx", "../src/**/*.stories.@(js|jsx|mjs|ts|tsx)"], - stories: ['../src/components/**/*.stories.@(ts|tsx)'], - addons: [ - '@storybook/addon-onboarding', - '@storybook/addon-links', - '@storybook/addon-essentials', - '@chromatic-com/storybook', - '@storybook/addon-interactions', - ], - framework: { - name: '@storybook/react-vite', - options: {}, - }, + // stories: ["../src/**/*.mdx", "../src/**/*.stories.@(js|jsx|mjs|ts|tsx)"], + stories: ['../src/components/**/*.stories.@(ts|tsx)'], + addons: [ + '@storybook/addon-onboarding', + '@storybook/addon-links', + '@storybook/addon-essentials', + '@chromatic-com/storybook', + '@storybook/addon-interactions', + ], + framework: { + name: '@storybook/react-vite', + options: {}, + }, }; export default config; diff --git a/README.md b/README.md index a300f1d..3f7fc29 100644 --- a/README.md +++ b/README.md @@ -6,10 +6,10 @@ NISA Invest is a personal finance platform for Muslim women. This project uses the following technologies: -- **Frontend Framework**: React -- **Language**: TypeScript -- **Build Tool**: Vite -- **UI Styling**: Tailwind CSS +- **Frontend Framework**: React +- **Language**: TypeScript +- **Build Tool**: Vite +- **UI Styling**: Tailwind CSS ## Getting Started @@ -40,11 +40,14 @@ npm install and include the following line: -- development: +- development: + ```env VITE_BASE_URL = "https://nisa-invest-tfb-be.vercel.app" ``` -- production: + +- production: + ```env VITE_BASE_URL = "https://nisa-invest-tfb-be.vercel.app" ``` @@ -63,4 +66,4 @@ Contributors should refer to our style guide for coding conventions and best pra Please make sure to read and follow the style guide before submitting any pull requests. -#Force redeploy \ No newline at end of file +#Force redeploy diff --git a/src/App.tsx b/src/App.tsx index a2d8a68..90ba826 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,48 +1,48 @@ -import { BrowserRouter } from 'react-router-dom' -import { useEffect } from 'react' -import AppRoutes from './routes/AppRoutes' -import { Footer } from '@/components/Footer/Footer' -import NavBar from './components/NavBar/NavBar' -import { supabase } from './lib/supabaseClient' -import { AuthChangeEvent, Session } from '@supabase/supabase-js' +import { BrowserRouter } from 'react-router-dom'; +import { useEffect } from 'react'; +import AppRoutes from './routes/AppRoutes'; +import { Footer } from '@/components/Footer/Footer'; +import NavBar from './components/NavBar/NavBar'; +import { supabase } from './lib/supabaseClient'; +import { AuthChangeEvent, Session } from '@supabase/supabase-js'; import { AuthProvider } from './AuthContext'; function App() { - useEffect(() => { - const { data: authListener } = supabase.auth.onAuthStateChange( - (event: AuthChangeEvent, session: Session | null) => { - if (event === 'SIGNED_IN') { - // Handle sign in - console.log('User signed in:', session?.user) - // You might want to update your app state or context here - } else if (event === 'SIGNED_OUT') { - // Handle sign out - console.log('User signed out') - // You might want to clear user data from your app state or context here - } - } - ) + useEffect(() => { + const { data: authListener } = supabase.auth.onAuthStateChange( + (event: AuthChangeEvent, session: Session | null) => { + if (event === 'SIGNED_IN') { + // Handle sign in + console.log('User signed in:', session?.user); + // You might want to update your app state or context here + } else if (event === 'SIGNED_OUT') { + // Handle sign out + console.log('User signed out'); + // You might want to clear user data from your app state or context here + } + } + ); - return () => { - authListener.subscription.unsubscribe() - } - }, []) + return () => { + authListener.subscription.unsubscribe(); + }; + }, []); - return ( - <> - - {' '} - {/* Wrap the entire app with AuthProvider */} - -
- - - -
-
-
- - ) + return ( + <> + + {' '} + {/* Wrap the entire app with AuthProvider */} + +
+ + + +
+
+
+ + ); } -export default App +export default App; diff --git a/src/components/Button/Button.tsx b/src/components/Button/Button.tsx index af83585..2e9434f 100644 --- a/src/components/Button/Button.tsx +++ b/src/components/Button/Button.tsx @@ -1,44 +1,46 @@ export interface ButtonProps { - /** - * Is this the principal call to action on the page? - */ - primary?: boolean; - /** - * What background color to use - */ - backgroundColor?: string; + /** + * Is this the principal call to action on the page? + */ + primary?: boolean; + /** + * What background color to use + */ + backgroundColor?: string; - fontFamily?:string; - /** - * How large should the button be? - */ - size?: 'small' | 'medium' | 'large'; - /** - * Button contents - */ - label: string; - /** - * Optional click handler - */ - onClick?: () => void; + fontFamily?: string; + /** + * How large should the button be? + */ + size?: 'small' | 'medium' | 'large'; + /** + * Button contents + */ + label: string; + /** + * Optional click handler + */ + onClick?: () => void; } export const Button = ({ - primary = true, - size = 'medium', - backgroundColor, - fontFamily, - label, - ...props + primary = true, + size = 'medium', + backgroundColor, + fontFamily, + label, + ...props }: ButtonProps) => { - return ( - - ); + return ( + + ); }; diff --git a/src/components/LoginForm/LoginForm.tsx b/src/components/LoginForm/LoginForm.tsx index b9b48fd..722a9af 100644 --- a/src/components/LoginForm/LoginForm.tsx +++ b/src/components/LoginForm/LoginForm.tsx @@ -1,99 +1,109 @@ -import { z } from 'zod' -import { zodResolver } from '@hookform/resolvers/zod' -import { useForm } from 'react-hook-form' -import { Button } from '@/components/ui/button' +import { z } from 'zod'; +import { zodResolver } from '@hookform/resolvers/zod'; +import { useForm } from 'react-hook-form'; +import { Button } from '@/components/ui/button'; import { - Form, - FormControl, - FormDescription, - FormField, - FormItem, - FormLabel, - FormMessage, -} from '@/components/ui/form' -import { Input } from '@/components/ui/input' -import { useNavigate } from 'react-router-dom' -import { signIn } from '@/lib/auth' -import { useState } from 'react' + Form, + FormControl, + FormDescription, + FormField, + FormItem, + FormLabel, + FormMessage, +} from '@/components/ui/form'; +import { Input } from '@/components/ui/input'; +import { useNavigate } from 'react-router-dom'; +import { signIn } from '@/lib/auth'; +import { useState } from 'react'; const formSchema = z.object({ - email: z.string().email({ - message: 'Please enter a valid email address.', - }), - password: z.string().min(8, { - message: 'Password must be at least 8 characters long.', - }), -}) + email: z.string().email({ + message: 'Please enter a valid email address.', + }), + password: z.string().min(8, { + message: 'Password must be at least 8 characters long.', + }), +}); export function LoginForm() { - const [isLoading, setIsLoading] = useState(false) - const [error, setError] = useState(null) - const navigate = useNavigate() + const [isLoading, setIsLoading] = useState(false); + const [error, setError] = useState(null); + const navigate = useNavigate(); - // 1. Define your form. - const form = useForm>({ - resolver: zodResolver(formSchema), - defaultValues: { - email: '', - password: '', - }, - }) + // 1. Define your form. + const form = useForm>({ + resolver: zodResolver(formSchema), + defaultValues: { + email: '', + password: '', + }, + }); - // 2. Define a submit handler. - async function onSubmit(values: z.infer) { - setIsLoading(true) - setError(null) - try { - await signIn(values.email, values.password) - navigate('/dashboard') - } catch (error) { - setError('Failed to sign in. Please check your credentials.') - console.error('Login error:', error) - } finally { - setIsLoading(false) - } - } + // 2. Define a submit handler. + async function onSubmit(values: z.infer) { + setIsLoading(true); + setError(null); + try { + await signIn(values.email, values.password); + navigate('/dashboard'); + } catch (error) { + setError('Failed to sign in. Please check your credentials.'); + console.error('Login error:', error); + } finally { + setIsLoading(false); + } + } - return ( -
- - {error &&
{error}
} - ( - - Your Email - - - - - - )} - /> - ( - - Your Password - - - - - - )} - /> -
- -
- - - ) + return ( +
+ + {error && ( +
{error}
+ )} + ( + + Your Email + + + + + + )} + /> + ( + + Your Password + + + + + + )} + /> +
+ +
+ + + ); } diff --git a/src/components/NavBar/NavBar.tsx b/src/components/NavBar/NavBar.tsx index 8911d48..8a6efac 100644 --- a/src/components/NavBar/NavBar.tsx +++ b/src/components/NavBar/NavBar.tsx @@ -12,18 +12,18 @@ function NavBar() { const { isLoggedIn } = useAuth(); // Use the isLoggedIn state from AuthContext return ( -
- - Nisa Invest +
+ + Nisa Invest {isMobile ? ( - - + ) : ( - + )}
); } -export default NavBar; \ No newline at end of file +export default NavBar; diff --git a/src/components/NavBar/NavItems.tsx b/src/components/NavBar/NavItems.tsx index ef1fc36..49607ed 100644 --- a/src/components/NavBar/NavItems.tsx +++ b/src/components/NavBar/NavItems.tsx @@ -1,144 +1,158 @@ -import { Link } from 'react-router-dom' +import { Link } from 'react-router-dom'; import { - NavigationMenu, - NavigationMenuContent, - NavigationMenuItem, - NavigationMenuLink, - NavigationMenuList, - NavigationMenuTrigger, -} from '@/components/ui/navigation-menu' -import { Button, buttonVariants } from '../ui/button' -import { useAuth } from '@/AuthContext' -import { signOut } from '@/lib/auth' + NavigationMenu, + NavigationMenuContent, + NavigationMenuItem, + NavigationMenuLink, + NavigationMenuList, + NavigationMenuTrigger, +} from '@/components/ui/navigation-menu'; +import { Button, buttonVariants } from '../ui/button'; +import { useAuth } from '@/AuthContext'; +import { signOut } from '@/lib/auth'; export interface NavItemsProps { - classNameValue: string - isLoggedIn: boolean + classNameValue: string; + isLoggedIn: boolean; } export const navItems = [ - { name: 'Home', path: '/' }, - { - name: 'About', - items: [ - { name: 'Our Story', path: '/about' }, - { name: 'Meet the Planners', path: '/advisors' }, - ], - }, - { - name: 'Knowledge Hub', - items: [ - { name: 'Book a Session', path: '/booking' }, - { name: 'Resources', path: '/resources' }, - { name: 'Podcast', path: '/podcast' }, - { name: 'FAQs', path: '/faq' }, - { name: 'Contact Us', path: '/contact' }, - ], - }, -] + { name: 'Home', path: '/' }, + { + name: 'About', + items: [ + { name: 'Our Story', path: '/about' }, + { name: 'Meet the Planners', path: '/advisors' }, + ], + }, + { + name: 'Knowledge Hub', + items: [ + { name: 'Book a Session', path: '/booking' }, + { name: 'Resources', path: '/resources' }, + { name: 'Podcast', path: '/podcast' }, + { name: 'FAQs', path: '/faq' }, + { name: 'Contact Us', path: '/contact' }, + ], + }, +]; function NavItems({ classNameValue, isLoggedIn }: NavItemsProps) { - const { setIsLoggedIn } = useAuth() + const { setIsLoggedIn } = useAuth(); - const handleLogout = async () => { - try { - await signOut() - setIsLoggedIn(false) - // window.location.href = '/'; // Redirect to home page after logout - } catch (error) { - console.error('Logout failed:', error) - } - } + const handleLogout = async () => { + try { + await signOut(); + setIsLoggedIn(false); + // window.location.href = '/'; // Redirect to home page after logout + } catch (error) { + console.error('Logout failed:', error); + } + }; - return ( - - - {navItems.map((item) => ( - - {item.items ? ( - <> - {item.name} - -
    - {item.items.map((subItem) => ( -
  • - - - {subItem.name} - - -
  • - ))} -
-
- - ) : ( - - - {item.name} - - - )} -
- ))} + return ( + + + {navItems.map((item) => ( + + {item.items ? ( + <> + + {item.name} + + +
    + {item.items.map((subItem) => ( +
  • + + + {subItem.name} + + +
  • + ))} +
+
+ + ) : ( + + + {item.name} + + + )} +
+ ))} - {isLoggedIn ? ( - <> - - - Account - - - - - - - ) : ( - <> - - - Login - - - - - Sign up - - - - )} + {isLoggedIn ? ( + <> + + + Account + + + + + + + ) : ( + <> + + + Login + + + + + Sign up + + + + )} - - - Book a Demo - - -
-
- ) + + + Book a Demo + + +
+
+ ); } -export default NavItems +export default NavItems; diff --git a/src/lib/supabaseClient.ts b/src/lib/supabaseClient.ts index 89f1679..733b874 100644 --- a/src/lib/supabaseClient.ts +++ b/src/lib/supabaseClient.ts @@ -1,6 +1,6 @@ -import { createClient } from '@supabase/supabase-js' +import { createClient } from '@supabase/supabase-js'; -const supabaseUrl = import.meta.env.VITE_SUPABASE_URL -const supabaseAnonKey = import.meta.env.VITE_SUPABASE_ANON_KEY +const supabaseUrl = import.meta.env.VITE_SUPABASE_URL; +const supabaseAnonKey = import.meta.env.VITE_SUPABASE_ANON_KEY; -export const supabase = createClient(supabaseUrl, supabaseAnonKey) \ No newline at end of file +export const supabase = createClient(supabaseUrl, supabaseAnonKey); diff --git a/src/pages/Advisors/Advisors.tsx b/src/pages/Advisors/Advisors.tsx index e43ab88..119d71a 100644 --- a/src/pages/Advisors/Advisors.tsx +++ b/src/pages/Advisors/Advisors.tsx @@ -5,66 +5,70 @@ import { Link } from 'react-router-dom'; import { advisorsData } from '@/components/AdvisorTabs/advisorsData'; import { Step } from '@/components/StepsCard/StepsCard'; import { - FaCalendarCheck, - FaClipboardList, - FaDesktop, - FaSeedling, + FaCalendarCheck, + FaClipboardList, + FaDesktop, + FaSeedling, } from 'react-icons/fa6'; const stepsList: Step[] = [ - { - icon: FaCalendarCheck, - number: 1, - description: 'Book your free guidance session', - }, - { - icon: FaDesktop, - number: 2, - description: 'Visit your interactive dashboard', - }, - { - icon: FaClipboardList, - number: 3, - description: 'View your meeting notes', - }, - { - icon: FaSeedling, - number: 4, - description: 'Give sadaqah jariyah together', - }, + { + icon: FaCalendarCheck, + number: 1, + description: 'Book your free guidance session', + }, + { + icon: FaDesktop, + number: 2, + description: 'Visit your interactive dashboard', + }, + { + icon: FaClipboardList, + number: 3, + description: 'View your meeting notes', + }, + { + icon: FaSeedling, + number: 4, + description: 'Give sadaqah jariyah together', + }, ]; function Advisors() { - return ( -
-
-
-

- Meet the Nisa Invest Planners -

-

- One-to-one financial guidance to support you at every stage -

- - Book your session - -
- -
-

How it works

-

- Bismillah, we will begin with a conversation, sister to sister -

-
-
- -
-
-
- ); + return ( +
+
+
+

+ Meet the Nisa Invest Planners +

+

+ One-to-one financial guidance to support you at every + stage +

+ + Book your session + +
+ +
+

+ How it works +

+

+ Bismillah, we will begin with a conversation, sister to + sister +

+
+
+ +
+
+
+ ); } export default Advisors; diff --git a/src/pages/Home/Home.tsx b/src/pages/Home/Home.tsx index 40f09e5..df57bb0 100644 --- a/src/pages/Home/Home.tsx +++ b/src/pages/Home/Home.tsx @@ -3,69 +3,70 @@ import { quotes } from '@/components/CarouselQuote/data'; import { CarouselTestimonial } from '@/components/CarouselTestimonial/CarouselTestimonial'; import { testimonials } from '@/components/CarouselTestimonial/Testimonials'; import { ProfileCard } from '@/components/ProfileCard/ProfileCard'; -import image from '/fahan.png' +import image from '/fahan.png'; import { Button } from '@/components/ui/button'; import { Link } from 'react-router-dom'; function Home() { - return ( -
- {/* Section #1 */} -
-
-
-
-

- Wealth management to help you tie your camel -

-

- Tailored personal finance by and for Muslim women -

-
-
- Picture of a cartoon camel -
-
-
- - -
-
-
- {/* Section #2 */} -
- -
- {/* Section #3 */} -
- -
- -
- -
-
- ); + return ( +
+ {/* Section #1 */} +
+
+
+
+

+ Wealth management to help you tie your camel +

+

+ Tailored personal finance by and for Muslim + women +

+
+
+ Picture of a cartoon camel +
+
+
+ + +
+
+
+ {/* Section #2 */} +
+ +
+ {/* Section #3 */} +
+ +
+ +
+ +
+
+ ); } export default Home; diff --git a/src/pages/Login/Login.tsx b/src/pages/Login/Login.tsx index a2b2d38..0dad4ee 100644 --- a/src/pages/Login/Login.tsx +++ b/src/pages/Login/Login.tsx @@ -7,63 +7,67 @@ import { Input } from '@/components/ui/input'; import { Link } from 'react-router-dom'; function Login() { - const [email, setEmail] = useState(''); - const [password, setPassword] = useState(''); - const [error, setError] = useState(''); - const navigate = useNavigate(); - const { setIsLoggedIn } = useAuth(); + const [email, setEmail] = useState(''); + const [password, setPassword] = useState(''); + const [error, setError] = useState(''); + const navigate = useNavigate(); + const { setIsLoggedIn } = useAuth(); - const handleSubmit = async (e: React.FormEvent) => { - e.preventDefault(); - setError(''); - try { - await signIn(email, password); - setIsLoggedIn(true); // Set login state to true - navigate('/'); // Redirect to home page or dashboard - } catch (err) { - setError('Invalid login credentials. Please try again.'); - console.error('Login error:', err); - } - }; + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + setError(''); + try { + await signIn(email, password); + setIsLoggedIn(true); // Set login state to true + navigate('/'); // Redirect to home page or dashboard + } catch (err) { + setError('Invalid login credentials. Please try again.'); + console.error('Login error:', err); + } + }; - return ( -
-
-
-

- Access Your Nisa Invest Account -

-

- Please enter your credentials below to log in -

+ return ( +
+
+
+

+ Access Your Nisa Invest Account +

+

+ Please enter your credentials below to log in +

+
+
+ {error && ( +
{error}
+ )} + setEmail(e.target.value)} + placeholder="Email" + required + /> + setPassword(e.target.value)} + placeholder="Password" + required + /> + +
+

+ I don't have an account yet.{' '} + +

+
-
- {error &&
{error}
} - setEmail(e.target.value)} - placeholder="Email" - required - /> - setPassword(e.target.value)} - placeholder="Password" - required - /> - -
-

- I don't have an account yet.{' '} - -

-
-
- ); + ); } -export default Login; \ No newline at end of file +export default Login; diff --git a/src/pages/SignUp/SignUp.tsx b/src/pages/SignUp/SignUp.tsx index 3e85561..fab6c3f 100644 --- a/src/pages/SignUp/SignUp.tsx +++ b/src/pages/SignUp/SignUp.tsx @@ -5,68 +5,72 @@ import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; function SignUp() { - const [email, setEmail] = useState(''); - const [password, setPassword] = useState(''); - const [error, setError] = useState(''); - const navigate = useNavigate(); + const [email, setEmail] = useState(''); + const [password, setPassword] = useState(''); + const [error, setError] = useState(''); + const navigate = useNavigate(); - const handleSubmit = async (e: React.FormEvent) => { - e.preventDefault(); - setError(''); - try { - await signUp(email, password); - // Redirect to login page or dashboard after successful sign-up - navigate('/login'); - } catch (err) { - setError('Sign-up failed. Please try again.'); - console.error('Sign-up error:', err); - } - }; + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + setError(''); + try { + await signUp(email, password); + // Redirect to login page or dashboard after successful sign-up + navigate('/login'); + } catch (err) { + setError('Sign-up failed. Please try again.'); + console.error('Sign-up error:', err); + } + }; - return ( -
-
-
-

- Thanks for your interest in Nisa Invest -

-

- Please fill out the details below to create an account -

+ return ( +
+
+
+

+ Thanks for your interest in Nisa Invest +

+

+ Please fill out the details below to create an account +

+
+
+ {error && ( +
{error}
+ )} +
+ setEmail(e.target.value)} + placeholder="Email" + required + className="w-full" + /> +
+
+ setPassword(e.target.value)} + placeholder="Password" + required + className="w-full" + /> +
+ +
+

+ I already have an account.{' '} + +

+
-
- {error &&
{error}
} -
- setEmail(e.target.value)} - placeholder="Email" - required - className="w-full" - /> -
-
- setPassword(e.target.value)} - placeholder="Password" - required - className="w-full" - /> -
- -
-

- I already have an account.{' '} - -

-
-
- ); + ); } -export default SignUp; \ No newline at end of file +export default SignUp;