<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Stefan Hodoroaba</title>
    <description>The latest articles on DEV Community by Stefan Hodoroaba (@stefan_hodoroaba).</description>
    <link>https://dev.to/stefan_hodoroaba</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1664199%2F76f46174-74f8-4538-8d77-deeb5c11b08f.png</url>
      <title>DEV Community: Stefan Hodoroaba</title>
      <link>https://dev.to/stefan_hodoroaba</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/stefan_hodoroaba"/>
    <language>en</language>
    <item>
      <title>Todo App in 3 hours</title>
      <dc:creator>Stefan Hodoroaba</dc:creator>
      <pubDate>Fri, 21 Jun 2024 20:14:42 +0000</pubDate>
      <link>https://dev.to/stefan_hodoroaba/todo-app-in-3-hours-4f33</link>
      <guid>https://dev.to/stefan_hodoroaba/todo-app-in-3-hours-4f33</guid>
      <description>&lt;h2&gt;
  
  
  Creating a Todo app with Refine and Supabase
&lt;/h2&gt;

&lt;p&gt;This article will cover the technical aspects of how I made a Todo app in a few hours using Refine and Supabase. I tried to take a few detours from the official way of doing things to showcase a few possible ways one can achieve the same result. &lt;br&gt;
All the code is available at &lt;a href="https://github.com/TheEmi/TodoRefine"&gt;https://github.com/TheEmi/TodoRefine&lt;/a&gt;&lt;br&gt;
You can see an instance of the app running at &lt;a href="https://todo-app-rosy-pi-29.vercel.app/"&gt;https://todo-app-rosy-pi-29.vercel.app/&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Getting started
&lt;/h2&gt;

&lt;p&gt;To begin, we will head to &lt;a href="//refine.new"&gt;refine.new&lt;/a&gt; which is a quick tool for creating the Refine project with the standard integrations already written. Selecting Vite as the react platform, Ant Design for the UI framework and Supabase for both backend and authentication. This will create a template that we can download and open to get a head-start on our project.&lt;/p&gt;

&lt;p&gt;Next, we will go to &lt;a href="//supabase.com"&gt;supabase.com&lt;/a&gt;, create an account and a project. &lt;/p&gt;

&lt;p&gt;To connect our Supabase instance we will store our SUPABASE_URL and SUPABASE_ANON_KEY in a .env file to avoid publishing this on GitHub. &lt;/p&gt;

&lt;p&gt;Here is what the .env in the root of the directory looks like:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;VITE_SUPABASE_URL=https://vlkudqvakedjdcnakadcnejj.supabase.co
VITE_SUPABASE_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InZsa3VkcXZxdWd6b189dnNjKJsdn6ImFub24iLCJpYXQiOjE3MTg0NTk1OTQsImV4cCI6MjAzNDAzNTU5NH0.7IRAWj13jrkiMCB_sUBEWJCd44XrC0SvZX-aHCO7ogw
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Next, we can use the environment variables in the ./utility/supabaseClient.ts:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const  SUPABASE_URL  =  import.meta.env.VITE_SUPABASE_URL;
const  SUPABASE_KEY  =  import.meta.env.VITE_SUPABASE_KEY;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  What do you want the project to do?
&lt;/h2&gt;

&lt;p&gt;Our configuration is done and now we need to have a clear vision of what our project will accomplish so we can model our database and build the views we need.&lt;/p&gt;

&lt;p&gt;I needed a Todo app to track daily activities and allow me to check tasks I've done. There are hundreds of other apps but the advantage is that I can customize it however I see fit. That being said I want 3 main sections:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Daily Todo&lt;/li&gt;
&lt;li&gt;Calendar with all days&lt;/li&gt;
&lt;li&gt;Default items&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here is a bit more detail:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;When we first log in I want to see today's checklist of activities I need to do. This list should be customizable and should contain a checkbox for when an activity is finished.&lt;/li&gt;
&lt;li&gt;A calendar view to navigate between days and plan activities ahead. Clicking a day will go to the same view we have at 1.&lt;/li&gt;
&lt;li&gt;Default items that will be allocated to each day created. This should be an editable list containing all recurring habits.
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;
  
  
  Database modeling
&lt;/h2&gt;

&lt;p&gt;Now that we have a clear view of what we need we can create tables and assign fields to them in Supabase.&lt;/p&gt;

&lt;p&gt;I will create 2 tables:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;todo: To store the activities of all days and all users.
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;create&lt;/span&gt;  &lt;span class="k"&gt;table&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;todo&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="nb"&gt;bigint&lt;/span&gt; &lt;span class="k"&gt;generated&lt;/span&gt; &lt;span class="k"&gt;by&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt;  &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="k"&gt;identity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="nb"&gt;timestamp&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nb"&gt;time&lt;/span&gt; &lt;span class="k"&gt;zone&lt;/span&gt; &lt;span class="k"&gt;not&lt;/span&gt;  &lt;span class="k"&gt;null&lt;/span&gt;  &lt;span class="k"&gt;default&lt;/span&gt;  &lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
&lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="n"&gt;uuid&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;  &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="n"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;uid&lt;/span&gt; &lt;span class="p"&gt;(),&lt;/span&gt;
&lt;span class="n"&gt;todo&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;  &lt;span class="k"&gt;default&lt;/span&gt;  &lt;span class="s1"&gt;'{}'&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="k"&gt;day&lt;/span&gt; &lt;span class="nb"&gt;date&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="k"&gt;constraint&lt;/span&gt; &lt;span class="n"&gt;todo_pkey&lt;/span&gt; &lt;span class="k"&gt;primary&lt;/span&gt; &lt;span class="k"&gt;key&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;tablespace&lt;/span&gt; &lt;span class="n"&gt;pg_default&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ol&gt;
&lt;li&gt;default_todo: To store default habits (one per user).
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;create&lt;/span&gt;  &lt;span class="k"&gt;table&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;default_todo&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="nb"&gt;bigint&lt;/span&gt; &lt;span class="k"&gt;generated&lt;/span&gt; &lt;span class="k"&gt;by&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt;  &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="k"&gt;identity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="nb"&gt;timestamp&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nb"&gt;time&lt;/span&gt; &lt;span class="k"&gt;zone&lt;/span&gt; &lt;span class="k"&gt;not&lt;/span&gt;  &lt;span class="k"&gt;null&lt;/span&gt;  &lt;span class="k"&gt;default&lt;/span&gt;  &lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
&lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="n"&gt;uuid&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;  &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="n"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;uid&lt;/span&gt; &lt;span class="p"&gt;(),&lt;/span&gt;
&lt;span class="k"&gt;defaults&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;  &lt;span class="k"&gt;default&lt;/span&gt;  &lt;span class="s1"&gt;'{}'&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="k"&gt;constraint&lt;/span&gt; &lt;span class="n"&gt;default_todo_pkey&lt;/span&gt; &lt;span class="k"&gt;primary&lt;/span&gt; &lt;span class="k"&gt;key&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;tablespace&lt;/span&gt; &lt;span class="n"&gt;pg_default&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Now we can create a default_todo row when a user registers by creating a database function and trigger on auth.users:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt;
&lt;span class="k"&gt;OR&lt;/span&gt; &lt;span class="k"&gt;REPLACE&lt;/span&gt; &lt;span class="k"&gt;FUNCTION&lt;/span&gt; &lt;span class="n"&gt;insert_default_todo&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;RETURNS&lt;/span&gt; &lt;span class="k"&gt;TRIGGER&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt;  &lt;span class="err"&gt;$$&lt;/span&gt;
&lt;span class="k"&gt;begin&lt;/span&gt; &lt;span class="k"&gt;insert&lt;/span&gt; &lt;span class="k"&gt;into&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;default_todo&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;defaults&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;values&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;NEW&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="s1"&gt;'{"items":[]}'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;NEW&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;$$&lt;/span&gt; &lt;span class="k"&gt;LANGUAGE&lt;/span&gt; &lt;span class="n"&gt;plpgsql&lt;/span&gt; &lt;span class="k"&gt;security&lt;/span&gt; &lt;span class="k"&gt;definer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TRIGGER&lt;/span&gt; &lt;span class="n"&gt;insert_default_todo_trigger&lt;/span&gt;
&lt;span class="k"&gt;after&lt;/span&gt; &lt;span class="k"&gt;insert&lt;/span&gt; &lt;span class="k"&gt;on&lt;/span&gt; &lt;span class="n"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;users&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="k"&gt;each&lt;/span&gt; &lt;span class="k"&gt;row&lt;/span&gt; &lt;span class="k"&gt;execute&lt;/span&gt; &lt;span class="k"&gt;procedure&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;insert_default_todo&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Don't forget about policies! This is where user data filtering will happen. We can set RLS (Row level security) to only allow &lt;strong&gt;insert&lt;/strong&gt; for authenticated users:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;alter&lt;/span&gt;  &lt;span class="n"&gt;policy&lt;/span&gt; &lt;span class="nv"&gt;"Enable insert for authenticated users only"&lt;/span&gt;
&lt;span class="k"&gt;on&lt;/span&gt;  &lt;span class="nv"&gt;"public"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;"todo"&lt;/span&gt;
&lt;span class="k"&gt;to&lt;/span&gt;  &lt;span class="n"&gt;authenticated&lt;/span&gt;
&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="k"&gt;check&lt;/span&gt;  &lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="k"&gt;true&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And data access for &lt;strong&gt;select&lt;/strong&gt; and &lt;strong&gt;update&lt;/strong&gt; only where  &lt;code&gt;user_id&lt;/code&gt; is the same as authenticated user id (&lt;code&gt;auth.id()&lt;/code&gt;)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;alter&lt;/span&gt;  &lt;span class="n"&gt;policy&lt;/span&gt; &lt;span class="nv"&gt;"Enable select for users based on user_id"&lt;/span&gt;
&lt;span class="k"&gt;on&lt;/span&gt;  &lt;span class="nv"&gt;"public"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;"todo"&lt;/span&gt;
&lt;span class="k"&gt;to&lt;/span&gt;  &lt;span class="k"&gt;public&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt;  &lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="p"&gt;((&lt;/span&gt;  &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;uid&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;uid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The last thing we will do on Supabase is create a database function to showcase them. In the SQL Editor we can write a function like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt;
&lt;span class="k"&gt;OR&lt;/span&gt; &lt;span class="k"&gt;REPLACE&lt;/span&gt; &lt;span class="k"&gt;FUNCTION&lt;/span&gt; &lt;span class="n"&gt;get_todo_by_day&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;current_day&lt;/span&gt; &lt;span class="nb"&gt;DATE&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;RETURNS&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt;  &lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="nb"&gt;BIGINT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="n"&gt;todo&lt;/span&gt; &lt;span class="n"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="k"&gt;DAY&lt;/span&gt; &lt;span class="nb"&gt;DATE&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="k"&gt;AS&lt;/span&gt;  &lt;span class="err"&gt;$$&lt;/span&gt;
&lt;span class="k"&gt;BEGIN&lt;/span&gt;
&lt;span class="k"&gt;RETURN&lt;/span&gt; &lt;span class="n"&gt;QUERY&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;day&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;todo&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;day&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;current_day&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;$$&lt;/span&gt; &lt;span class="k"&gt;LANGUAGE&lt;/span&gt; &lt;span class="n"&gt;plpgsql&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This function takes a DATE as a parameter to return the row for that day. This is replaceable by a select where(day = parameter_day) but we want to experiment with RPC functions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Refine
&lt;/h2&gt;

&lt;p&gt;We can now start on the frontend by first defining our Refine component in the App.tsx file. This is what tells Refine where to access its data. For us, we will simply use it to define the pages we want to see in the layout and implement a custom data query. Note the data, auth, router, and notification provider are also defined here.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;Refine&lt;/span&gt;
    &lt;span class="na"&gt;dataProvider=&lt;/span&gt;&lt;span class="s"&gt;{dataProvider(supabaseClient)}&lt;/span&gt;
    &lt;span class="na"&gt;authProvider=&lt;/span&gt;&lt;span class="s"&gt;{authProvider}&lt;/span&gt;
    &lt;span class="na"&gt;routerProvider=&lt;/span&gt;&lt;span class="s"&gt;{routerBindings}&lt;/span&gt;
    &lt;span class="na"&gt;notificationProvider=&lt;/span&gt;&lt;span class="s"&gt;{useNotificationProvider}&lt;/span&gt;
    &lt;span class="na"&gt;resources=&lt;/span&gt;&lt;span class="s"&gt;{[&lt;/span&gt;
        &lt;span class="err"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;name:&lt;/span&gt;  &lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="na"&gt;today&lt;/span&gt;&lt;span class="err"&gt;",&lt;/span&gt;
            &lt;span class="na"&gt;list:&lt;/span&gt;  &lt;span class="err"&gt;"/&lt;/span&gt;&lt;span class="na"&gt;today&lt;/span&gt;&lt;span class="err"&gt;",&lt;/span&gt;
            &lt;span class="na"&gt;meta:&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt;
                &lt;span class="na"&gt;label:&lt;/span&gt;  &lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="na"&gt;Today&lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt;
            &lt;span class="err"&gt;}&lt;/span&gt;
        &lt;span class="err"&gt;},&lt;/span&gt;
        &lt;span class="err"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;name:&lt;/span&gt;  &lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="na"&gt;todo&lt;/span&gt;&lt;span class="err"&gt;",&lt;/span&gt;
            &lt;span class="na"&gt;list:&lt;/span&gt;  &lt;span class="err"&gt;"/&lt;/span&gt;&lt;span class="na"&gt;todo&lt;/span&gt;&lt;span class="err"&gt;",&lt;/span&gt;
            &lt;span class="na"&gt;meta:&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt;
                &lt;span class="na"&gt;label:&lt;/span&gt;  &lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="na"&gt;Calendar&lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt;
            &lt;span class="err"&gt;}&lt;/span&gt;
        &lt;span class="err"&gt;},&lt;/span&gt;
        &lt;span class="err"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;name:&lt;/span&gt;  &lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="na"&gt;default_todo&lt;/span&gt;&lt;span class="err"&gt;",&lt;/span&gt;
            &lt;span class="na"&gt;list:&lt;/span&gt;  &lt;span class="err"&gt;"/&lt;/span&gt;&lt;span class="na"&gt;default_todo&lt;/span&gt;&lt;span class="err"&gt;",&lt;/span&gt;
            &lt;span class="na"&gt;meta:&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt;
                &lt;span class="na"&gt;label:&lt;/span&gt;  &lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="na"&gt;Default&lt;/span&gt; &lt;span class="na"&gt;Todo&lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt;
            &lt;span class="err"&gt;}&lt;/span&gt;
        &lt;span class="err"&gt;},&lt;/span&gt;
    &lt;span class="err"&gt;]}&lt;/span&gt;
    &lt;span class="na"&gt;options=&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;
        &lt;span class="na"&gt;syncWithLocation:&lt;/span&gt;  &lt;span class="na"&gt;true&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;warnWhenUnsavedChanges:&lt;/span&gt;  &lt;span class="na"&gt;true&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;useNewQueryKeys:&lt;/span&gt;  &lt;span class="na"&gt;true&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;projectId:&lt;/span&gt;  &lt;span class="err"&gt;"8&lt;/span&gt;&lt;span class="na"&gt;BijMs-4mgC2j-TUz0Rv&lt;/span&gt;&lt;span class="err"&gt;",&lt;/span&gt;
        &lt;span class="na"&gt;title:&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt; &lt;span class="na"&gt;text:&lt;/span&gt;  &lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="na"&gt;Todo&lt;/span&gt; &lt;span class="na"&gt;App&lt;/span&gt;&lt;span class="err"&gt;",&lt;/span&gt; &lt;span class="na"&gt;icon:&lt;/span&gt;  &lt;span class="err"&gt;&amp;lt;&lt;/span&gt;&lt;span class="na"&gt;CheckSquareOutlined&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt; },
    }}
&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Another change we need in the App.tsx file is to define the components for our routes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;Route&lt;/span&gt;  &lt;span class="na"&gt;path=&lt;/span&gt;&lt;span class="s"&gt;"/today"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;Route&lt;/span&gt;  &lt;span class="na"&gt;index&lt;/span&gt;  &lt;span class="na"&gt;element=&lt;/span&gt;&lt;span class="s"&gt;{&amp;lt;TodayTodo&lt;/span&gt;  &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;}  /&amp;gt;
&lt;span class="nt"&gt;&amp;lt;/Route&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;Route&lt;/span&gt;  &lt;span class="na"&gt;path=&lt;/span&gt;&lt;span class="s"&gt;"/todo"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;Route&lt;/span&gt;  &lt;span class="na"&gt;index&lt;/span&gt;  &lt;span class="na"&gt;element=&lt;/span&gt;&lt;span class="s"&gt;{&amp;lt;TodoCalendar&lt;/span&gt;  &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;}  /&amp;gt;
&lt;span class="nt"&gt;&amp;lt;/Route&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;Route&lt;/span&gt;  &lt;span class="na"&gt;path=&lt;/span&gt;&lt;span class="s"&gt;"/default_todo"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;Route&lt;/span&gt;  &lt;span class="na"&gt;index&lt;/span&gt;  &lt;span class="na"&gt;element=&lt;/span&gt;&lt;span class="s"&gt;{&amp;lt;Default_todo&lt;/span&gt;  &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;}  /&amp;gt;
&lt;span class="nt"&gt;&amp;lt;/Route&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can see the pages in the Sider of our Layout and accessing them will load our page components. Now let's move on to actually writing the code for each page!&lt;/p&gt;

&lt;h3&gt;
  
  
  Default Todo items
&lt;/h3&gt;

&lt;p&gt;To fetch the data we can call &lt;code&gt;useList({ resource:  "default_todo" })&lt;/code&gt; and access the first element knowing each user will only have one default_todo row.&lt;/p&gt;

&lt;p&gt;This page needs to display current items, with the possibility of adding and removing items. Luckily, Ant Design has the Form.List component that implements this. We just need to combine it with whatever style we want to achieve. I chose to use a Table instead of mapping over all fields of the Form. Here is a basic hierarchy to better understand this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;Form&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;Form.List&amp;gt;&lt;/span&gt;
        {(fields, operator) =&amp;gt;{
            return (&lt;span class="nt"&gt;&amp;lt;&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;Table&lt;/span&gt; &lt;span class="na"&gt;dataSource=&lt;/span&gt;&lt;span class="s"&gt;{fields}&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            ............ Column configuration ..............
                &lt;span class="nt"&gt;&amp;lt;/Table&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;Button&lt;/span&gt; &lt;span class="na"&gt;onClick=&lt;/span&gt;&lt;span class="s"&gt;(&lt;/span&gt; &lt;span class="na"&gt;()=&lt;/span&gt;&lt;span class="err"&gt;&amp;gt; &lt;/span&gt;&lt;span class="s"&gt;operator.add();)/&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/&amp;gt;&lt;/span&gt;
                );
            }
        }
    &lt;span class="nt"&gt;&amp;lt;/Form.List&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;Form&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv7vi96dmgrvxtl52gjnl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv7vi96dmgrvxtl52gjnl.png" alt="Default Todo items" width="800" height="327"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Today's todo
&lt;/h3&gt;

&lt;p&gt;We will first begin by returning the current day from the todo table by using the database function we created.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;supabaseClient&lt;/span&gt;
&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rpc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;get_todo_by_day&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;current_day&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="nf"&gt;dayjs&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;YYYY-MM-DD&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If this returns an empty array we will attempt to get the default_todo items for the current user and create an entry in the todo list and set the form to display these items as well.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;supabaseClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;default_todo&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;*&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
        &lt;span class="nf"&gt;mutateCreate&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
            &lt;span class="na"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;todo&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;values&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
                &lt;span class="na"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;defaults&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; 
                &lt;span class="na"&gt;day&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="nf"&gt;dayjs&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;YYYY-MM-DD&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="p"&gt;});&lt;/span&gt;
        &lt;span class="nx"&gt;form&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setFieldsValue&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;defaults&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;defaults&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm0acwt3nwi4g53799ij8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm0acwt3nwi4g53799ij8.png" alt="Today's items" width="800" height="306"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Calendar view
&lt;/h3&gt;

&lt;p&gt;We will begin by adding a Calendar component from Ant Design and define the onSelect and cellRender properties.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;Calendar&lt;/span&gt;  &lt;span class="na"&gt;onSelect=&lt;/span&gt;&lt;span class="s"&gt;{handleSelect}&lt;/span&gt;  &lt;span class="na"&gt;cellRender=&lt;/span&gt;&lt;span class="s"&gt;{cellRender}&lt;/span&gt;  &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;cellRender will filter the data for any given calendar cell and return a list with the items on that day.&lt;/p&gt;

&lt;p&gt;handleSelect will just set the selected date to pass it to a child component:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;SpecificTodo&lt;/span&gt;  &lt;span class="na"&gt;day=&lt;/span&gt;&lt;span class="s"&gt;{selectedDay}&lt;/span&gt;  &lt;span class="na"&gt;returnToCalendar=&lt;/span&gt;&lt;span class="s"&gt;{callbackReturn}&lt;/span&gt;  &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This component is a copy of the today's todo component with the option to return to calender via a callback function.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh3mvfyw2es2aijogllju.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh3mvfyw2es2aijogllju.png" alt="Calendar view" width="800" height="674"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With this we have a functional Todo app with authentication, user-specific data filtering, and infinite opportunities for the future, in just 3 hours of configuring and coding.&lt;/p&gt;

&lt;p&gt;Hope this helps anyone who wanted to take Refine or Supabase for a spin!&lt;/p&gt;

</description>
      <category>refine</category>
      <category>react</category>
      <category>supabase</category>
    </item>
  </channel>
</rss>
