> For the complete documentation index, see [llms.txt](https://reactjs.koida.tech/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://reactjs.koida.tech/database-in-application/hands-on-note-taking-app.md).

# Hands-on: Note Taking App

## Step-by-Step Guide to Creating a Note Taking App with Vite, React, and Supabase

This guide provides a step-by-step update for enhancing the **Note-Taking App** by integrating authentication, user session management, and CRUD operations with a **Supabase database**. The updated app will feature a **Header with the app title and user email**, a **SignUp page**, and a **Dashboard** where users can **see, create, update, and delete notes**.

### Step 1: Install Node.js

Before you start, ensure you have [Node.js](https://nodejs.org/) installed. You can check your installation by running:

```bash
node -v
npm -v
```

If Node.js is not installed, download and install it from the official website.

### Step 2: Create a New Vite Project

Vite is a fast build tool for modern web applications. To create a new Vite-powered React project, run the following command at Git-bash terminal or PowerShell terminal:

```bash
npm create vite@latest react-note-taker-app --template react
```

Alternatively, if you are using Yarn or PNPM, you can use:

```bash
yarn create vite@latest react-note-taker-app --template react
```

```bash
pnpm create vite@latest react-note-taker-app --template react
```

### Step 3: Move into the Project Directory

Once the project is created, navigate into the project folder:

```bash
cd react-note-taker-app
```

### Step 4: Install Dependencies

Run the following command to install the necessary dependencies:

```bash
npm install
```

This will install all required packages specified in `package.json`.

### Step 5: Start the Development Server

Then, open Visual Studio Code IDE and open a new terminal . To start the development server and preview the application, run:

```bash
npm run dev
```

This command will provide a local development URL (e.g., `http://localhost:5173/`) where you can see your app in action.

Now, delete all unnecessary code snippet in App.jsx  to get started a new React App creation.

### Step 6: Connect to Supabase

#### a. Create a Supabase Project

1. Go to [Supabase](https://supabase.com/) and sign up.
2. Create a new project and note down the `Project URL` and `Anon Key`.

#### b. Install Supabase Client

In your project directory, install the Supabase client:

```bash
npm install @supabase/supabase-js
```

#### c. Configure Supabase

Create a <mark style="color:orange;">**`.env`**</mark> file in the root of your project and add:

```
VITE_SUPABASE_URL=your_supabase_project_url
VITE_SUPABASE_ANON_KEY=your_supabase_anon_key
```

Then, in `src/`<mark style="color:orange;">`supabaseClient.js`</mark>, initialize Supabase:

```jsx
import { createClient } from '@supabase/supabase-js';

const supabaseUrl = import.meta.env.VITE_SUPABASE_URL;
const supabaseAnonKey = import.meta.env.VITE_SUPABASE_ANON_KEY;

export const supabase = createClient(supabaseUrl, supabaseAnonKey);
```

Now your project is set up with Vite, React, and Supabase!

### Step 7: Create a Database Table for NoteTaking App

Navigate to the **Supabase Dashboard** → **Table Editor** and create a new table called <mark style="color:orange;">notes</mark> with the following columns:

* `id` (UUID, Primary Key, Default: `gen_random_uuid()`)
* `title` (Text, Not Null)
* `content` (Text, Not Null)
* `date` (Timestamp, Default: `now()`)
* `iscompleted` (Boolean, Default: `false`)
* `user_id` (UUID, Foreign Key referencing auth.users, <mark style="color:red;">**Not Null**</mark>)

### Step 8: Create Authentication Components(final)

To enable user authentication, install:

```bash
npm install @supabase/auth-ui-react @supabase/auth-helpers-react
```

Then, Create `src/components/Auth.jsx` to handle **SignUp, SignIn, SignOut, Email Confirmation, and Reset Password**.

#### a. Create Auth Component for SignUp and SignIn(final)

In `src/components/Auth.jsx`, add:

{% tabs %}
{% tab title="Before" %}

```jsx
// Before
import { useEffect, useState } from 'react';
import { supabase } from '../supabase';

export default function Auth() {
  const [session, setSession] = useState(null);

  useEffect(() => {
    supabase.auth.getSession().then(({ data: { session } }) => {
      setSession(session);
    });
    supabase.auth.onAuthStateChange((_event, session) => {
      setSession(session);
    });
  }, []);

  const signIn = async () => {
    await supabase.auth.signInWithOAuth({ provider: 'google' });
  };

  const signOut = async () => {
    await supabase.auth.signOut();
  };

  return (
    <div>
      {session ? (
        <button onClick={signOut}>Sign Out</button>
      ) : (
        <button onClick={signIn}>Sign In with Google</button>
      )}
    </div>
  );
}
```

{% endtab %}

{% tab title="After" %}

```jsx
// After
import { useState } from 'react';
import { supabase } from '../supabaseClient';
import { Button, Form, Container } from 'react-bootstrap';

const Auth = () => {
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');
  const [loading, setLoading] = useState(false);

  const handleSignUp = async () => {
    setLoading(true);
    const { error } = await supabase.auth.signUp({ email, password });
    if (error) alert(error.message);
    setLoading(false);
  };

  const handleSignIn = async () => {
    setLoading(true);
    const { error } = await supabase.auth.signInWithPassword({ email, password });
    if (error) alert(error.message);
    setLoading(false);
  };

  return (
    <Container className="mt-4">
      <h2>Sign In / Sign Up</h2>
      <Form>
        <Form.Group className="mb-3">
          <Form.Control type="email" placeholder="Email" value={email} onChange={(e) => setEmail(e.target.value)} required />
        </Form.Group>
        <Form.Group className="mb-3">
          <Form.Control type="password" placeholder="Password" value={password} onChange={(e) => setPassword(e.target.value)} required />
        </Form.Group>
        <Button variant="primary" onClick={handleSignUp} disabled={loading}>Sign Up</Button>
        <Button variant="secondary" onClick={handleSignIn} disabled={loading} className="ms-2">Sign In</Button>
      </Form>
    </Container>
  );
};

export default Auth;
```

{% endtab %}

{% tab title="final" %}

#### a. Create an Authentication Handler

Create a new file `src/auth.js` to handle user authentication:

```javascript
import { useState } from 'react';
import { supabase } from '../supabaseClient';
import { Button, Form, Container } from 'react-bootstrap';
import { useNavigate } from 'react-router-dom';

const Auth = () => {
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');
  const [loading, setLoading] = useState(false);
  const navigate = useNavigate();

  const handleSignUp = async () => {
    setLoading(true);
    const { error } = await supabase.auth.signUp({ email, password });
    if (error) alert(error.message);
    setLoading(false);
  };

  const handleSignIn = async () => {
    setLoading(true);
    const { error } = await supabase.auth.signInWithPassword({ email, password });
    if (error) alert(error.message);
    else navigate('/dashboard');
    setLoading(false);
  };

  return (
    <Container className="mt-4">
      <h2>Sign In / Sign Up</h2>
      <Form>
        <Form.Group className="mb-3">
          <Form.Control type="email" placeholder="Email" value={email} onChange={(e) => setEmail(e.target.value)} required />
        </Form.Group>
        <Form.Group className="mb-3">
          <Form.Control type="password" placeholder="Password" value={password} onChange={(e) => setPassword(e.target.value)} required />
        </Form.Group>
        <Button variant="primary" onClick={handleSignUp} disabled={loading}>Sign Up</Button>
        <Button variant="secondary" onClick={handleSignIn} disabled={loading} className="ms-2">Sign In</Button>
      </Form>
    </Container>
  );
};

export default Auth;
```

#### b. Create Auth Component for SignUp and SignIn (optional)

In `src/auth.js`, update the code to use `auth.js` functions:

Now, the authentication logic is centralized in `auth.js`, making it reusable and maintainable. The application supports sign-up, sign-in, sign-out, password reset, and fetching the authenticated user. Let me know if you need any refinements!(optional)
{% endtab %}
{% endtabs %}

#### b. Implement Password Reset (optional)

In `src/components/ResetPassword.jsx`, add:

```jsx
import { useState } from 'react';
import { supabase } from '../supabase';

export default function ResetPassword() {
  const [email, setEmail] = useState('');

  const resetPassword = async () => {
    await supabase.auth.resetPasswordForEmail(email);
    alert('Password reset email sent!');
  };

  return (
    <div>
      <input placeholder='Email' onChange={(e) => setEmail(e.target.value)} />
      <button onClick={resetPassword}>Reset Password</button>
    </div>
  );
}
```

### Step 9: Update App Header

Modify `src/components/Header.jsx` to show the app title and logged-in user email.

```jsx
import { useEffect, useState } from 'react';
import { Navbar, Container, Button } from 'react-bootstrap';
import { supabase } from '../supabaseClient';

const Header = () => {
  const [user, setUser] = useState(null);

  useEffect(() => {
    const fetchUser = async () => {
      const { data: { user } } = await supabase.auth.getUser();
      setUser(user);
    };
    fetchUser();
  }, []);

  const handleSignOut = async () => {
    await supabase.auth.signOut();
    window.location.reload();
  };

  return (
    <Navbar bg="dark" variant="dark">
      <Container>
        <Navbar.Brand>Note Taking App</Navbar.Brand>
        {user && (
          <span className="text-white">{user.email} <Button variant="danger" onClick={handleSignOut}>Sign Out</Button></span>
        )}
      </Container>
    </Navbar>
  );
};

export default Header;
```

### Step 10: Create the Dashboard Page

In `src/pages/Dashboard.jsx`, add:

```jsx
import { useEffect, useState } from 'react';
import { supabase } from '../supabaseClient';
import { Container, Button, Form } from 'react-bootstrap';
import Note from '../components/Note';

const Dashboard = () => {
  const [notes, setNotes] = useState([]);
  const [title, setTitle] = useState('');
  const [content, setContent] = useState('');

  useEffect(() => {
    fetchNotes();
  }, []);

  const fetchNotes = async () => {
    const { data, error } = await supabase.from('notes').select('*');
    if (!error) setNotes(data);
  };

  const addNote = async (e) => {
    e.preventDefault();
    const { data, error } = await supabase.from('notes').insert([{ title, content, date: new Date(), iscompleted: false }]);
    if (!error) setNotes([...notes, ...data]);
    setTitle('');
    setContent('');
  };

  const updateNote = async (id, iscompleted) => {
    await supabase.from('notes').update({ iscompleted }).match({ id });
    setNotes(notes.map(note => note.id === id ? { ...note, iscompleted } : note));
  };

  const deleteNote = async (id) => {
    await supabase.from('notes').delete().match({ id });
    setNotes(notes.filter(note => note.id !== id));
  };

  return (
    <Container className="mt-4">
      <h2>Manage Your Notes</h2>
      <Form onSubmit={addNote}>
        <Form.Group>
          <Form.Control type="text" placeholder="Title" value={title} onChange={(e) => setTitle(e.target.value)} required />
        </Form.Group>
        <Form.Group>
          <Form.Control as="textarea" rows={3} placeholder="Content" value={content} onChange={(e) => setContent(e.target.value)} required />
        </Form.Group>
        <Button type="submit">Add Note</Button>
      </Form>
      <div className="mt-4">
        {notes.map(note => (
          <Note key={note.id} note={note} onUpdate={updateNote} onDelete={deleteNote} />
        ))}
      </div>
    </Container>
  );
};

export default Dashboard;
```

Now your NoteTaker app includes authentication, password reset, and a fully functional dashboard to create, view, update, and delete notes using Supabase!

### Step 11: Update App Header(final)

Modify `src/components/Header.jsx` to show the app title and logged-in user email, with a **Sign Out** button.

{% tabs %}
{% tab title="Before" %}

```jsx
// src/components/Header.jsx
import { useEffect, useState } from 'react';
import { Navbar, Container, Button } from 'react-bootstrap';
import { supabase } from '../supabaseClient';
import { useNavigate } from 'react-router-dom';

const Header = () => {
  const [user, setUser] = useState(null);
  const navigate = useNavigate();

  useEffect(() => {
    const fetchUser = async () => {
      const { data: { user } } = await supabase.auth.getUser();
      setUser(user);
    };
    fetchUser();
  }, []);

  const handleSignOut = async () => {
    await supabase.auth.signOut();
    navigate('/');
  };

  return (
    <Navbar bg="dark" variant="dark">
      <Container>
        <Navbar.Brand>Note Taking App</Navbar.Brand>
        {user && (
          <span className="text-white">{user.email} <Button variant="danger" onClick={handleSignOut}>Sign Out</Button></span>
        )}
      </Container>
    </Navbar>
  );
};

export default Header;
```

{% endtab %}

{% tab title="Final" %}

```jsx
// src/components/Header.jsx
import { useEffect, useState } from 'react'
import { Navbar, Container, Button } from 'react-bootstrap'
import supabase from '../supabaseClient'
import { useNavigate } from 'react-router-dom'

const Header = () => {
  const [user, setUser] = useState(null)
  const navigate = useNavigate()

  useEffect(() => {
    const fetchUser = async () => {
      const {
        data: { user },
      } = await supabase.auth.getUser()
      setUser(user)
    }
    fetchUser()
  }, [])

  const handleSignOut = async () => {
    await supabase.auth.signOut()
    setUser(null)
    navigate('/')
  }

  return (
    <Navbar bg="dark" variant="dark">
      <Container>
        <Navbar.Brand>Note Taking App</Navbar.Brand>
        {user ? (
          <span className="text-white mr-2">
            {user.email}{' '}
            <Button variant="danger" onClick={handleSignOut}>
              Sign Out
            </Button>
          </span>
        ) : null}
      </Container>
    </Navbar>
  )
}

export default Header

```

{% endtab %}
{% endtabs %}

### Step 12: Create a Dashboard Component with Card Display for Notes

Modify `src/pages/Dashboard.jsx` to manage notes and display them in card format.

```jsx
import { useEffect, useState } from 'react';
import { supabase } from '../supabaseClient';
import { Container, Button, Card } from 'react-bootstrap';
import { useNavigate } from 'react-router-dom';

const Dashboard = () => {
  const [notes, setNotes] = useState([]);
  const navigate = useNavigate();

  useEffect(() => {
    const checkUser = async () => {
      const { data: { user } } = await supabase.auth.getUser();
      if (!user) navigate('/');
    };
    checkUser();
    fetchNotes();
  }, []);

  const fetchNotes = async () => {
    const { data, error } = await supabase.from('notes').select('*');
    if (!error) setNotes(data);
  };

  const deleteNote = async (id) => {
    await supabase.from('notes').delete().match({ id });
    setNotes(notes.filter(note => note.id !== id));
  };

  return (
    <Container className="mt-4">
      <h2>Manage Your Notes</h2>
      <div className="mt-4">
        {notes.map(note => (
          <Card key={note.id} className="mb-3">
            <Card.Body>
              <Card.Title>{note.title}</Card.Title>
              <Card.Text><strong>Date:</strong> {new Date(note.date).toLocaleDateString()}</Card.Text>
              <Card.Text>{note.content}</Card.Text>
              <Card.Text><strong>Status:</strong> {note.iscompleted ? 'Completed' : 'Not Completed'}</Card.Text>
              <Button variant="danger" onClick={() => deleteNote(note.id)}><i className="bi bi-trash"></i> Delete</Button>
            </Card.Body>
          </Card>
        ))}
      </div>
    </Container>
  );
};

export default Dashboard;
```

### Step 13: Create a Parent Component `App.jsx (final)`

Create `src/App.jsx` to integrate the **Header, Dashboard, User Authentication, and Routes**.

```jsx
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import Auth from './components/Auth';
import Header from './components/Header';
import Dashboard from './pages/Dashboard';

function App() {
  return (
    <Router>
      <Header />
      <Routes>
        <Route path="/" element={<Auth />} />
        <Route path="/dashboard" element={<Dashboard />} />
      </Routes>
    </Router>
  );
}

export default App;
```

### Step 14: Create a Dashboard Component with Card Display for Notes(final)

Modify `src/pages/Dashboard.jsx` to manage notes and display them in card format.

{% tabs %}
{% tab title="Before" %}

```jsx
// src/components/Dashboard.jsx
import { useEffect, useState } from 'react';
import { supabase } from '../supabaseClient';
import { Container, Button, Card } from 'react-bootstrap';
import { useNavigate } from 'react-router-dom';

const Dashboard = () => {
  const [notes, setNotes] = useState([]);
  const navigate = useNavigate();

  useEffect(() => {
    const checkUser = async () => {
      const { data: { user } } = await supabase.auth.getUser();
      if (!user) navigate('/');
    };
    checkUser();
    fetchNotes();
  }, []);

  const fetchNotes = async () => {
    const { data, error } = await supabase.from('notes').select('*');
    if (!error) setNotes(data);
  };

  return (
    <Container className="mt-4">
      <h2>Manage Your Notes</h2>
      <div className="mt-4">
        {notes.map(note => (
          <Card key={note.id} className="mb-3">
            <Card.Body>
              <Card.Title>{note.title}</Card.Title>
              <Card.Text><strong>Date:</strong> {new Date(note.date).toLocaleDateString()}</Card.Text>
              <Card.Text>{note.content}</Card.Text>
              <Card.Text><strong>Status:</strong> {note.iscompleted ? 'Completed' : 'Not Completed'}</Card.Text>
            </Card.Body>
          </Card>
        ))}
      </div>
    </Container>
  );
};

export default Dashboard;
```

{% endtab %}

{% tab title="After(final)" %}

````jsx
// src/components/Dashboard.jsx

import { useEffect, useState } from 'react'
import  supabase from '../supabaseClient'
import { Container, Button, Card } from 'react-bootstrap'
import { useNavigate } from 'react-router-dom'
import FormNote from '../components/FormNote'

const Dashboard = () => {
  const [notes, setNotes] = useState([])
  const [editNote, setEditNote] = useState(null)
  const navigate = useNavigate()

  useEffect(() => {
    const checkUser = async () => {
      const {
        data: { user },
      } = await supabase.auth.getUser()
      if (!user) navigate('/')
    }
    checkUser()
    fetchNotes()
  }, [])

  const fetchNotes = async () => {
    const { data, error } = await supabase.from('knotes').select('*')
    if (!error) setNotes(data)
  }

  const deleteNote = async (id) => {
    await supabase.from('knotes').delete().match({ id })
    setNotes(notes.filter((note) => note.id !== id))
  }

  const toggleCompletion = async (id, iscompleted) => {
    await supabase
      .from('knotes')
      .update({ iscompleted: !iscompleted })
      .match({ id })
    setNotes(
      notes.map((note) =>
        note.id === id ? { ...note, iscompleted: !iscompleted } : note,
      ),
    )
  }

  return (
    <Container className="mt-4">
      <h2>Manage Your Notes</h2>
      <FormNote
        refreshNotes={fetchNotes}
        existingNote={editNote}
        clearEdit={() => setEditNote(null)}
      />
      <div className="mt-4">
        {notes.map((note) => (
          <Card key={note.id} className="mb-3">
            <Card.Body>
              <Card.Title>{note.title}</Card.Title>
              <Card.Text>
                <strong>Date:</strong>{' '}
                {new Date(note.date).toLocaleDateString()}
              </Card.Text>
              <Card.Text>{note.content}</Card.Text>
              <Card.Text>
                <strong>Status:</strong>{' '}
                {note.iscompleted ? 'Completed' : 'Not Completed'}
              </Card.Text>
              <Button
                variant="success"
                onClick={() => toggleCompletion(note.id, note.iscompleted)}>
                {note.iscompleted ? 'Mark as Incomplete' : 'Mark as Completed'}
              </Button>
              <Button
                variant="warning"
                onClick={() => setEditNote(note)}
                className="ms-2">
                Update
              </Button>
              <Button
                variant="danger"
                onClick={() => deleteNote(note.id)}
                className="ms-2">
                <i className="bi bi-trash"></i> Delete
              </Button>
            </Card.Body>
          </Card>
        ))}
      </div>
    </Container>
  )
}

export default Dashboard

```
````

{% endtab %}
{% endtabs %}

### Step 15:  Create a Note Taking Feature(final)

In `src/components/FormNote.jsx`, add:

```jsx
// src/components/FormNote.jsx
import { useState } from 'react'
import  supabase from '../supabaseClient'
import { Form, Button } from 'react-bootstrap'

const FormNote = ({ refreshNotes }) => {
  const [title, setTitle] = useState('')
  const [content, setContent] = useState('')

  const addNote = async (e) => {
    e.preventDefault()
    const { data, error } = await supabase
      .from('notes')
      .insert([{ title, content, date: new Date(), iscompleted: false }])
    if (!error) {
      setTitle('')
      setContent('')
      refreshNotes()
    }
  }

  return (
    <Form onSubmit={addNote} className="mb-4">
      <Form.Group className="mb-3">
        <Form.Control
          type="text"
          placeholder="Title"
          value={title}
          onChange={(e) => setTitle(e.target.value)}
          required
        />
      </Form.Group>
      <Form.Group className="mb-3">
        <Form.Control
          as="textarea"
          rows={3}
          placeholder="Content"
          value={content}
          onChange={(e) => setContent(e.target.value)}
          required
        />
      </Form.Group>
      <Button type="submit" variant="primary">
        Add Note
      </Button>
    </Form>
  )
}

export default FormNote
```

The updated app now includes:

* **A `FormNote` component to handle both note creation and updating.**
* **Dashboard functionality to create, update, and delete notes.**
* **A new Update button that allows users to edit existing notes.**

### Step 16: Run the Application

Start the development server:

```bash
npm run dev
```

#### Conclusion

The updated app now includes **a parent `App.jsx` component** that manages routing and integrates **authentication, dashboard, and header** components for a complete user experience.

### Challenge: Step A: Create a Note Taking Feature

In `src/components/FormNote.jsx`, add:

```jsx
import { useState } from 'react'
import  supabase from '../supabaseClient'
import { Form, Button } from 'react-bootstrap'

const FormNote = ({ refreshNotes }) => {
  const [title, setTitle] = useState('')
  const [content, setContent] = useState('')

  const addNote = async (e) => {
    e.preventDefault()
    const { data, error } = await supabase
      .from('notes')
      .insert([{ title, content, date: new Date(), iscompleted: false }])
    if (!error) {
      setTitle('')
      setContent('')
      refreshNotes()
    }
  }

  return (
    <Form onSubmit={addNote} className="mb-4">
      <Form.Group className="mb-3">
        <Form.Control
          type="text"
          placeholder="Title"
          value={title}
          onChange={(e) => setTitle(e.target.value)}
          required
        />
      </Form.Group>
      <Form.Group className="mb-3">
        <Form.Control
          as="textarea"
          rows={3}
          placeholder="Content"
          value={content}
          onChange={(e) => setContent(e.target.value)}
          required
        />
      </Form.Group>
      <Button type="submit" variant="primary">
        Add Note
      </Button>
    </Form>
  )
}

export default FormNote
```

### Step B: SQL Command Operation on Supabase DB table  for Policies Setting&#x20;

```sql
// Some code
-- Enable extensions required for UUID generation
create extension if not exists "pgcrypto";

-- Create the 'notes' table
create table public.notes (
    id uuid primary key default gen_random_uuid(),
    title text not null,
    content text not null,
    date timestamp default now() not null,
    isCompleted boolean default false not null,
    user_id uuid references auth.users(id) on delete cascade not null
);

-- Enable Row Level Security (RLS) on the 'notes' table
alter table public.notes enable row level security;

-- Create policies to allow users to interact only with their own notes

-- Allow users to insert their own notes
create policy "Allow users to insert their own notes"
on public.notes
for insert
with check (auth.uid() = user_id);

-- Allow users to read only their own notes
create policy "Allow users to read their own notes"
on public.notes
for select
using (auth.uid() = user_id);

-- Allow users to update only their own notes
create policy "Allow users to update their own notes"
on public.notes
for update
using (auth.uid() = user_id);

-- Allow users to delete only their own notes
create policy "Allow users to delete their own notes"
on public.notes
for delete
using (auth.uid() = user_id);
```

### Step C: Fetch and Display Notes

In `src/components/NoteList.js`:

```jsx
import { useEffect, useState } from 'react';
import { supabase } from '../supabase';

export default function NoteList() {
  const [notes, setNotes] = useState([]);

  useEffect(() => {
    const fetchNotes = async () => {
      const { data, error } = await supabase.from('notes').select('*');
      if (!error) setNotes(data);
    };
    fetchNotes();
  }, []);

  return (
    <ul>
      {notes.map((note) => (
        <li key={note.id}>{note.title}: {note.content}</li>
      ))}
    </ul>
  );
}
```

Now your NoteTaker app is fully functional with authentication, database integration, and note management!

### Step D: Create a Database Table in Supabase

#### a. Access Supabase Table Editor

1. Go to [Supabase](https://supabase.com/) and log in.
2. Open your project dashboard.
3. Navigate to the **Table Editor** on the left sidebar.
4. Click **New Table** .

#### b. Define Table Schema

Create a new table named `notes` with the following columns:

* **id** (UUID, Primary Key, Default: `gen_random_uuid()`, Not Null)
* **title** (Text, Not Null)
* **content** (Text, Not Null)
* **date** (Timestamp, Default: `now()`, Not Null)
* **isCompleted** (Boolean, Default: `false`, Not Null)
* **user\_id** (UUID, Foreign Key referencing `auth.users.id`, Not Null)

#### c. Save and Deploy

* Click **Save** to create the table.
* Ensure the table permissions allow `Authenticated` users to perform CRUD operations.

#### d. Configure Row-Level Security (RLS)

1. Navigate to the **Authentication** → **Policies** section.
2. Click **New Policy** and select the `notes` table.
3. Add a rule that allows users to access only their own notes:

```sql
CREATE POLICY "Enable read access for authenticated users" 
ON "public"."notes" 
FOR SELECT USING (auth.uid() = user_id);
```

4. Add policies for insert, update, and delete accordingly.

Now, your Supabase database table is set up and secured!

As an alternative better way, use the SQL command to create the `notes` table in Supabase with **Row Level Security (RLS)** enabled:

```sql
sql
-- Enable extensions required for UUID generation
create extension if not exists "pgcrypto";

-- Create the 'notes' table
create table public.notes (
    id uuid primary key default gen_random_uuid(),
    title text not null,
    content text not null,
    date timestamp default now() not null,
    isCompleted boolean default false not null,
    user_id uuid references auth.users(id) on delete cascade not null
);

-- Enable Row Level Security (RLS) on the 'notes' table
alter table public.notes enable row level security;

-- Create policies to allow users to interact only with their own notes

-- Allow users to insert their own notes
create policy "Allow users to insert their own notes"
on public.notes
for insert
with check (auth.uid() = user_id);

-- Allow users to read only their own notes
create policy "Allow users to read their own notes"
on public.notes
for select
using (auth.uid() = user_id);

-- Allow users to update only their own notes
create policy "Allow users to update their own notes"
on public.notes
for update
using (auth.uid() = user_id);

-- Allow users to delete only their own notes
create policy "Allow users to delete their own notes"
on public.notes
for delete
using (auth.uid() = user_id);
```

#### UUID Setting in RLS Policies

We have to set the column in the database table with a default of auth.uid().

* Type: uuid&#x20;
* Default Value: auth.uid()

<figure><img src="/files/EoPAOQXF1kf7jc6y7uH0" alt=""><figcaption></figcaption></figure>

#### SQL Command Explanation

* The `id` column is a UUID primary key generated using `gen_random_uuid()`.
* The `user_id` column references the `auth.users.id` field, ensuring that each note belongs to a specific authenticated user.
* **RLS is enabled** to ensure users can only access their own data.
* Four **security policies** are created to allow authenticated users to insert, select, update, and delete only their own notes.

You can execute this SQL command in the **SQL Editor** of your Supabase project to create and secure the `notes` table. 🚀

### Step E: Integrate Components into App.jsx

#### a.  See step F to create `App.jsx`

Modify `src/App.jsx` to integrate authentication and note-taking functionality:

#### b. Update `Dashboard.jsx`

Ensure `Dashboard.jsx` manages note creation, retrieval, update, and deletion:

### Final Steps

1. Run `npm run dev` to start the development server.
2. Test authentication and note management features.
3. Deploy the project using **Vercel** or **Netlify** for hosting.

Now your **Vite React Note Taker App** is fully functional with authentication and CRUD operations using Supabase! 🚀

Repeat Step 13 to accomodate sign out functionality.

### <mark style="color:red;">Note on  403 Forbidden Error</mark>

<details>

<summary>Supabase Policies</summary>

The `403 Forbidden` error typically indicates that your Supabase client does not have the necessary permissions to insert data into the `notes` table. Here are steps to troubleshoot and resolve this issue:

#### **Step 1: Check Supabase Policies**

1. Go to your **Supabase Dashboard**.
2. Navigate to **Database** > **Tables** > **notes**.
3. Click on **Row Level Security (RLS)**.
4. If RLS is **enabled**, ensure you have a policy that allows authenticated users to insert data. If there's no policy, add the following policy:

   **Create a policy for INSERT**

   * **Name**: Allow Insert for Authenticated Users
   * **Target**: INSERT
   * **Expression**: `auth.uid() IS NOT NULL <--- default = auth.uid()`

   This policy ensures only signed-in users can insert new notes.

#### **Step 2: Verify Supabase API Key Usage**

1. Open your `.env` file and confirm that you are using the correct **Anon Key**:

   ```jsx
   /.env file
   VITE_SUPABASE_URL=your_supabase_url
   VITE_SUPABASE_ANON_KEY=your_supabase_anon_key
   ```
2. If your Supabase project requires **Service Role Key** for inserting data, switch to using the **Service Role Key** in a backend function instead of using it directly in frontend code (which is unsafe).

</details>

### Step F: Integrate Components into App.jsx

#### a. Create `App.jsx`

Modify `src/App.jsx` to integrate authentication and note-taking functionality:

{% tabs %}
{% tab title="Before" %}

```jsx
//src/App.jsx
import { useState, useEffect } from 'react';
import { supabase } from './supabase';
import Auth from './components/Auth';
import Dashboard from './pages/Dashboard';

export default function App() {
  const [session, setSession] = useState(null);

  useEffect(() => {
    supabase.auth.getSession().then(({ data: { session } }) => {
      setSession(session);
    });
    supabase.auth.onAuthStateChange((_event, session) => {
      setSession(session);
    });
  }, []);

  return (
    <div>
      {session ? <Dashboard session={session} /> : <Auth />}
    </div>
  );
}
```

{% endtab %}

{% tab title="Final" %}

````jsx
// src/App.jsx

import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import Auth from './components/Auth';
import Header from './components/Header';
import Dashboard from './pages/Dashboard';

function App() {
  return (
    <Router>
      <Header />
      <Routes>
        <Route path="/" element={<Auth />} />
        <Route path="/dashboard" element={<Dashboard />} />
      </Routes>
    </Router>
  );
}

export default App;

```
````

{% endtab %}
{% endtabs %}

#### b. Update `Dashboard.jsx`

Ensure `Dashboard.jsx` includes a sign-out button and correctly saves notes:

{% tabs %}
{% tab title="Before" %}

```jsx
// src/pages/Dashboard.jsx
import { useEffect, useState } from 'react';
import { supabase } from '../supabase';

export default function Dashboard({ session }) {
  const [notes, setNotes] = useState([]);
  const [title, setTitle] = useState('');
  const [content, setContent] = useState('');

  useEffect(() => {
    const fetchNotes = async () => {
      const { data, error } = await supabase.from('notes').select('*').eq('user_id', session.user.id);
      if (!error) setNotes(data);
    };
    fetchNotes();
  }, [session]);

  const createNote = async () => {
    const { data, error } = await supabase.from('notes').insert([
      { title, content, user_id: session.user.id }
    ]);
    if (!error) setNotes([...notes, data[0]]);
  };

  const signOut = async () => {
    await supabase.auth.signOut();
  };

  return (
    <div>
      <h1>Dashboard</h1>
      <button onClick={signOut}>Sign Out</button>
      <input placeholder='Title' value={title} onChange={(e) => setTitle(e.target.value)} />
      <textarea placeholder='Content' value={content} onChange={(e) => setContent(e.target.value)} />
      <button onClick={createNote}>Create Note</button>
      <ul>
        {notes.map((note) => (
          <li key={note.id}>{note.title}: {note.content}</li>
        ))}
      </ul>
    </div>
  );
}
```

{% endtab %}

{% tab title="After" %}

````jsx
// src/pages/Dashboard.jsx
import { useEffect, useState } from 'react'
import  supabase from '../supabaseClient'
import { Container, Button, Card } from 'react-bootstrap'
import { useNavigate } from 'react-router-dom'
import FormNote from '../components/FormNote'

const Dashboard = () => {
  const [notes, setNotes] = useState([])
  const [editNote, setEditNote] = useState(null)
  const navigate = useNavigate()

  useEffect(() => {
    const checkUser = async () => {
      const {
        data: { user },
      } = await supabase.auth.getUser()
      if (!user) navigate('/')
    }
    checkUser()
    fetchNotes()
  }, [])

  const fetchNotes = async () => {
    const { data, error } = await supabase.from('knotes').select('*')
    if (!error) setNotes(data)
  }

  const deleteNote = async (id) => {
    await supabase.from('knotes').delete().match({ id })
    setNotes(notes.filter((note) => note.id !== id))
  }

  const toggleCompletion = async (id, iscompleted) => {
    await supabase
      .from('knotes')
      .update({ iscompleted: !iscompleted })
      .match({ id })
    setNotes(
      notes.map((note) =>
        note.id === id ? { ...note, iscompleted: !iscompleted } : note,
      ),
    )
  }

  return (
    <Container className="mt-4">
      <h2>Manage Your Notes</h2>
      <FormNote
        refreshNotes={fetchNotes}
        existingNote={editNote}
        clearEdit={() => setEditNote(null)}
      />
      <div className="mt-4">
        {notes.map((note) => (
          <Card key={note.id} className="mb-3">
            <Card.Body>
              <Card.Title>{note.title}</Card.Title>
              <Card.Text>
                <strong>Date:</strong>{' '}
                {new Date(note.date).toLocaleDateString()}
              </Card.Text>
              <Card.Text>{note.content}</Card.Text>
              <Card.Text>
                <strong>Status:</strong>{' '}
                {note.iscompleted ? 'Completed' : 'Not Completed'}
              </Card.Text>
              <Button
                variant="success"
                onClick={() => toggleCompletion(note.id, note.iscompleted)}>
                {note.iscompleted ? 'Mark as Incomplete' : 'Mark as Completed'}
              </Button>
              <Button
                variant="warning"
                onClick={() => setEditNote(note)}
                className="ms-2">
                Update
              </Button>
              <Button
                variant="danger"
                onClick={() => deleteNote(note.id)}
                className="ms-2">
                <i className="bi bi-trash"></i> Delete
              </Button>
            </Card.Body>
          </Card>
        ))}
      </div>
    </Container>
  )
}

export default Dashboard

```
````

{% endtab %}
{% endtabs %}

c.  Update `Header.jsx`

```jsx
// src/components/Header.jsx
import { useEffect, useState } from 'react'
import { Navbar, Container, Button } from 'react-bootstrap'
import supabase from '../supabaseClient'
import { useNavigate } from 'react-router-dom'

const Header = () => {
  const [user, setUser] = useState(null)
  const navigate = useNavigate()

  useEffect(() => {
    const fetchUser = async () => {
      const {
        data: { user },
      } = await supabase.auth.getUser()
      setUser(user)
    }
    fetchUser()
  }, [])

  const handleSignOut = async () => {
    await supabase.auth.signOut()
    setUser(null)
    navigate('/')
  }

  return (
    <Navbar bg="dark" variant="dark">
      <Container>
        <Navbar.Brand>Note Taking App</Navbar.Brand>
        {user ? (
          <span className="text-white mr-2">
            {user.email}{' '}
            <Button variant="danger" onClick={handleSignOut}>
              Sign Out
            </Button>
          </span>
        ) : null}
      </Container>
    </Navbar>
  )
}

export default Header

```

### Step G: Modify `FormNote.jsx` to Include Date Input and Completion Checkbox

In `src/components/FormNote.jsx`, update the form to include an input for the creation date and a checkbox for task completion:

{% tabs %}
{% tab title="Before" %}

```jsx
// src/components/FormNote.jsx
import { useState } from 'react';
import { supabase } from '../supabase';

export default function NoteForm({ onNoteAdded }) {
  const [title, setTitle] = useState('');
  const [content, setContent] = useState('');
  const [date, setDate] = useState(new Date().toISOString().split('T')[0]);
  const [isCompleted, setIsCompleted] = useState(false);

  const createNote = async () => {
    const { data, error } = await supabase.from('notes').insert([
      { title, content, date, isCompleted }
    ]);
    if (!error) {
      onNoteAdded(data[0]);
      setTitle('');
      setContent('');
      setDate(new Date().toISOString().split('T')[0]);
      setIsCompleted(false);
    }
  };

  return (
    <div>
      <input placeholder='Title' value={title} onChange={(e) => setTitle(e.target.value)} />
      <textarea placeholder='Content' value={content} onChange={(e) => setContent(e.target.value)} />
      <input type='date' value={date} onChange={(e) => setDate(e.target.value)} />
      <label>
        <input type='checkbox' checked={isCompleted} onChange={(e) => setIsCompleted(e.target.checked)} />
        Completed
      </label>
      <button onClick={createNote}>Create Note</button>
    </div>
  );
}
```

{% endtab %}

{% tab title="After" %}

````jsx
// src/components/FormNote.jsx 
import { useState } from 'react'
import  supabase from '../supabaseClient'
import { Form, Button } from 'react-bootstrap'

const FormNote = ({ refreshNotes, existingNote, clearEdit }) => {
  const [title, setTitle] = useState(existingNote ? existingNote.title : '')
  const [content, setContent] = useState(
    existingNote ? existingNote.content : '',
  )

  const handleSubmit = async (e) => {
    e.preventDefault()
    if (existingNote) {
      await supabase
        .from('knotes')
        .update({ title, content })
        .match({ id: existingNote.id })
    } else {
      await supabase
        .from('knotes')
        .insert([{ title, content, date: new Date(), iscompleted: false }])
    }
    setTitle('')
    setContent('')
    refreshNotes()
    if (existingNote) clearEdit()
  }

  return (
    <Form onSubmit={handleSubmit} className="mb-4">
      <Form.Group className="mb-3">
        <Form.Control
          type="text"
          placeholder="Title"
          value={title}
          onChange={(e) => setTitle(e.target.value)}
          required
        />
      </Form.Group>
      <Form.Group className="mb-3">
        <Form.Control
          as="textarea"
          rows={3}
          placeholder="Content"
          value={content}
          onChange={(e) => setContent(e.target.value)}
          required
        />
      </Form.Group>
      <Button type="submit" variant="primary">
        {existingNote ? 'Update Note' : 'Add Note'}
      </Button>
      {existingNote && (
        <Button variant="secondary" onClick={clearEdit} className="ms-2">
          Cancel
        </Button>
      )}
    </Form>
  )
}

export default FormNote

```
````

{% endtab %}
{% endtabs %}

### Step H: Modify `NoteList.jsx` to Display Date, Completion Checkbox, and Delete Button

In `src/components/NoteList.jsx`, update the list to show the note creation date, allow marking completion, and add a delete button:

```jsx
import { useEffect, useState } from 'react';
import { supabase } from '../supabase';
import 'bootstrap-icons/font/bootstrap-icons.css';

export default function NoteList() {
  const [notes, setNotes] = useState([]);

  useEffect(() => {
    const fetchNotes = async () => {
      const { data, error } = await supabase.from('notes').select('*');
      if (!error) setNotes(data);
    };
    fetchNotes();
  }, []);

  const deleteNote = async (id) => {
    await supabase.from('notes').delete().match({ id });
    setNotes(notes.filter(note => note.id !== id));
  };

  return (
    <ul>
      {notes.map((note) => (
        <li key={note.id}>
          <h3>{note.title}</h3>
          <p>{note.content}</p>
          <p><strong>Date Created:</strong> {note.date}</p>
          <label>
            <input type='checkbox' checked={note.isCompleted} readOnly />
            Completed
          </label>
          <button onClick={() => deleteNote(note.id)}>
            <i className='bi bi-trash'></i>
          </button>
        </li>
      ))}
    </ul>
  );
}
```

### Step I ::

### Step J: Run and Test Your Application

1. Start your development server with `npm run dev`.
2. Add new notes using the form, ensuring they store `date` and `isCompleted`.
3. Ensure the delete button removes notes successfully.
4. Verify that notes are stored and retrieved correctly from Supabase.

Now your **Vite React Note Taker App** includes a date input for note creation, a checkbox for marking completion, and a delete button using Bootstrap icons! 🚀
