- Development
Before you get started, you will need to have the following tools installed on your machine:
- Node.js (version 12 or later)
- pnpm (version 5 or later) or npm or yarn (version 6 or later)
- Git (optional, but recommended for version control)
This repository includes a list of suggested VS Code extensions. It's a good idea to use VS Code and accept its suggestion to install them, as they'll help with development.
The project uses environmental variables for configuration. You can set the
environmental variables in a .env
file in the root directory of the
project. The .env
file should contain key-value pairs in the following
format:
NEXT_PUBLIC_SITE_URL
(required): The URL of the frontend App of the project.
Adding a new environmental variable requires a zod schema update in the
env
folder and a new entry in theschema.js
file in theserverSchema
variable orclientSchema
variable.
You can run the project locally by
https://github.com/timelessco/bookmark-tags.git
cd bookmark-tags
pnpm install
This will download and install all the required dependencies for the project.
You need to create a .env
file and add all the environmental variables as per
the env.local.txt
file
pnpm dev
Open http://localhost:3000 with your browser to see the result.
You can start editing the page by modifying src/pages/index.js
. The page
auto-updates as you edit the file
To build the project to a production environment, you can use the
pnpm build
to build the production-ready version of the project. This will create a
.next
directory with the compiled code and static assets.
Run the above built application locally using
pnpm start
Guide on how to deploy Next.js to various hosting providers.
Check for all the below errors in one command using Turbo Repo
pnpm lint
AutoFix all the linting errors in one command using Turbo Repo
pnpm format
Prettier is used to format code. It should be applied automatically when you save files in VS Code or make a Git commit.
Check the formatting errors
pnpm lint:prettier
AutoFix the formatting errors
pnpm format:prettier
This package includes several forms of linting to enforce consistent code quality and styling. Each should be shown in VS Code, and can be run manually on the command-line:
Extends all the necessary rulesets from eslint-config-canonical for the Next.js project that lints JavaScript and TypeScript source files
Check for the linting errors
pnpm lint:eslint
AutoFix the linting errors
pnpm format:eslint
(Stylelint): Checks all css files
Check the css linting errors
pnpm lint:csslint
AutoFix the css linting errors
pnpm format:csslint
(Markdownlint): Checks all Markdown files
Check the markdown linting errors
pnpm lint:csslint
AutoFix the markdown linting errors
pnpm format:csslint
(TypeScript): Checks all TypeScript files
Check TypeScript types
pnpm lint:types
(knip): Checks all unused dependencies, exports & types
Check the spelling errors
pnpm lint:knip
(cspell): Spell checks across all source files
Check the spelling errors
pnpm lint:spelling
(npm-package-json-lint): Lints the package.json
file
Check the package.json linting errors
pnpm lint:package-json
Run the test suite
pnpm test
To enable google login please reffer supabase google login
Once you have created a new project in supabase and added the env as per
env.local.txt
file you need to fo the following
Create the following tables in supabase using the following sql commands in the supabase sql editor
create table
public.bookmark_tags (
id bigint generated by default as identity,
created_at timestamp with time zone null default now(),
bookmark_id bigint null,
tag_id bigint null,
user_id uuid null,
constraint bookmark_tags_pkey primary key (id),
constraint bookmark_tags_bookmark_id_fkey foreign key (bookmark_id) references bookmarks_table (id),
constraint bookmark_tags_tag_id_fkey foreign key (tag_id) references tags (id),
constraint bookmark_tags_user_id_fkey foreign key (user_id) references auth.users (id)
) tablespace pg_default;
create table
public.bookmarks_table (
id bigint generated by default as identity,
user_id uuid not null,
inserted_at timestamp with time zone not null default timezone ('utc'::text, now()),
title extensions.citext null,
url text null,
description text null,
"ogImage" text null,
screenshot text null,
category_id bigint not null default '0'::bigint,
trash boolean not null default false,
type text null,
meta_data jsonb null,
constraint todos_pkey primary key (id),
constraint bookmarks_table_category_id_fkey foreign key (category_id) references categories (id),
constraint bookmarks_table_user_id_fkey foreign key (user_id) references profiles (id)
) tablespace pg_default;
create unique index unique_url_category_id on public.bookmarks_table using btree (url, category_id)
where
(
(category_id is not null)
and (category_id <> 0)
) tablespace pg_default;
create index if not exists idx_title_description on public.bookmarks_table using btree (title, description) tablespace pg_default;
create table
public.categories (
id bigint generated by default as identity,
created_at timestamp with time zone null default now(),
category_name text null,
user_id uuid null,
category_slug character varying not null,
is_public boolean not null default false,
icon character varying null default 'file'::character varying,
category_views json null default '{ "moodboardColumns": [ 30 ], "cardContentViewArray": ["cover", "title", "info"], "bookmarksView": "moodboard", "sortBy": "date-sort-acending" }'::json,
order_index bigint null,
icon_color text null default '#000000'::text,
constraint categories_pkey primary key (id),
constraint categories_category_slug_key unique (category_slug),
constraint categories_user_id_fkey foreign key (user_id) references profiles (id)
) tablespace pg_default;
-- inserts the uncategorised collection into the table
insert into categories (id, category_name, category_slug) values (0, '00uncategorized', '00uncategorized');
create table
public.profiles (
id uuid not null,
email text null,
user_name character varying null,
profile_pic character varying null,
bookmarks_view json null default '{ "moodboardColumns": [ 30 ], "cardContentViewArray": ["cover", "title", "info"], "bookmarksView": "moodboard", "sortBy": "date-sort-acending" }'::json,
category_order bigint[] null,
constraint profiles_pkey primary key (id),
constraint profiles_email_key unique (email),
constraint profiles_user_name_key unique (user_name),
constraint profiles_id_fkey foreign key (id) references auth.users (id),
constraint bookmarks_view_check check (
(
(
(bookmarks_view -> 'moodboardColumns'::text) is not null
)
and (
(bookmarks_view -> 'cardContentViewArray'::text) is not null
)
and (
(bookmarks_view -> 'bookmarksView'::text) is not null
)
and ((bookmarks_view -> 'sortBy'::text) is not null)
)
)
) tablespace pg_default;
create table
public.shared_categories (
id bigint generated by default as identity,
created_at timestamp with time zone null default now(),
category_id bigint not null,
email character varying null,
edit_access boolean not null default false,
user_id uuid not null,
category_views json not null default '{ "moodboardColumns": [ 30 ], "cardContentViewArray": [ "cover", "title", "info" ], "bookmarksView": "moodboard", "sortBy": "date-sort-acending" }'::json,
is_accept_pending boolean null default true,
constraint shared_categories_pkey primary key (id),
constraint shared_categories_category_id_fkey foreign key (category_id) references categories (id),
constraint shared_categories_user_id_fkey foreign key (user_id) references profiles (id)
) tablespace pg_default;
create table
public.tags (
id bigint generated by default as identity,
created_at timestamp with time zone null default now(),
name text null,
user_id uuid null,
constraint tags_pkey primary key (id),
constraint tags_user_id_fkey foreign key (user_id) references auth.users (id)
) tablespace pg_default;
In supabase storage create the following buckets bookmarks
, user_profile
and
files
and have these buckets as public.
For search to work we need to first enable the pg_trgm
extensions under you
supabase projects extensions. After this you need to create a fuction with the
following query
CREATE OR REPLACE FUNCTION public.search_bookmarks("search_text" varchar)
RETURNS TABLE (
id int8,
user_id uuid,
inserted_at timestamptz,
title citext,
url text,
description text,
ogImage text,
screenshot text,
category_id int8,
trash bool,
type text,
meta_data jsonb
)
LANGUAGE plpgsql
AS $$
BEGIN
RETURN QUERY
SELECT *
FROM bookmarks_table b
WHERE
(search_text % ANY(STRING_TO_ARRAY(b.title || b.description, ' ')))
OR EXISTS (
SELECT 1
FROM jsonb_each_text(b.meta_data) x(key, value)
WHERE key = 'img_caption' AND value ILIKE '%' || search_text || '%'
);
END;
$$;
Create a trigger with the following query
-- inserts a row into public.users
create or replace function public.handle_new_user()
returns trigger
language plpgsql
security definer set search_path = public
as $$
begin
insert into public.profiles (id,email)
values (new.id, new.email);
return new;
end;
$$;
-- trigger the function every time a user is created
drop trigger on_auth_user_created on auth.users;
create trigger on_auth_user_created
after insert on auth.users
for each row execute procedure public.handle_new_user();