Skip to content

Commit

Permalink
Merge pull request #121 from premieroctet/feat/add-bookmark-button-im…
Browse files Browse the repository at this point in the history
…prove-ux

Feat: Add "Add bookmark button"
  • Loading branch information
quentingrchr authored Jun 13, 2024
2 parents 74938e4 + 2db7fe6 commit 2c4b133
Show file tree
Hide file tree
Showing 9 changed files with 147 additions and 79 deletions.
23 changes: 11 additions & 12 deletions src/components/Button.tsx
Original file line number Diff line number Diff line change
@@ -1,30 +1,29 @@
'use client';

import React, { forwardRef } from 'react';
import { AiOutlineLoading3Quarters as LoadingIcon } from '@react-icons/all-files/ai/AiOutlineLoading3Quarters';
import clsx from 'clsx';
import { VariantProps, cva } from 'class-variance-authority';
import clsx from 'clsx';
import React, { forwardRef } from 'react';

const buttonVariants = cva(
'rounded-md font-semibold focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 flex items-center justify-center gap-2 cursor-pointer',
{
variants: {
variant: {
default:
'ring-1 shadow-sm bg-violet-600 hover:bg-violet-700 text-white ring-violet-600',
default: 'shadow-sm bg-violet-600 hover:bg-violet-700 text-white',
outline:
'ring-1 shadow-sm bg-transparent hover:bg-violet-700 text-violet-700 ring-1 ring-violet-600 hover:bg-violet-100 hover:text-white',
'border border-1 shadow-sm bg-transparent hover:bg-violet-700 text-violet-700 border-violet-600 hover:text-white box-border',
destructive:
'ring-1 shadow-sm bg-red-700 hover:bg-red-600 text-white ring-red-700',
'border-1 shadow-sm bg-red-700 hover:bg-red-600 text-white border-red-700',
destructiveOutline:
'ring-1 shadow-sm bg-transparent hover:bg-red-700 text-red-700 ring-1 ring-red-700 hover:bg-red-100 hover:text-white',
'border-1 shadow-sm bg-transparent text-red-700 border-1 border-red-700 hover:bg-red-100 hover:text-white',
success:
'ring-1 shadow-sm bg-[#4ade80] hover:bg-[#3dba6b] text-white ring-[#4ade80] hover:ring-[#3dba6b]',
'border-1 shadow-sm bg-[#4ade80] hover:bg-[#3dba6b] text-white border-[#4ade80] hover:border-[#3dba6b]',
ghost:
'bg-transparent text-violet-700 ring-1 ring-transparent hover:bg-violet-100 hover:ring-violet-100',
'bg-transparent text-violet-700 border-1 border-transparent hover:bg-violet-100 hover:border-violet-100',
destructiveGhost:
'bg-transparent text-red-700 ring-1 ring-transparent hover:bg-red-100 hover:ring-red-100',
link: 'ring-0 text-gray-700 text-sm underline hover:text-gray-400 font-light !px-0 !font-normal',
'bg-transparent text-red-700 border-1 border-transparent hover:bg-red-100 hover:border-red-100',
link: 'border-0 text-gray-700 text-sm underline hover:text-gray-400 !px-0 !font-normal',
},
size: {
sm: 'py-1 px-4 text-sm',
Expand Down Expand Up @@ -82,7 +81,7 @@ const Button = forwardRef<HTMLButtonElement, ButtonProps>((props, ref) => {
[disabledClass]: disabled,
},
{
'w-full': fullWidth,
'w-full box-border': fullWidth,
},
className
)}
Expand Down
6 changes: 3 additions & 3 deletions src/components/auth/LoginForm.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
'use client';
import { routes } from '@/core/constants';
import message from '@/messages/en';
import { EmailProvider, getEmailProvider } from '@/utils/getEmailProvider';
import { CheckCircleIcon } from '@heroicons/react/24/solid';
import Link from 'next/link';
import { signIn } from 'next-auth/react';
import Link from 'next/link';
import { useSearchParams } from 'next/navigation';
import { useState } from 'react';
import { useMutation } from 'react-query';
import Button from '../Button';
import { Input } from '../Input';
import message from '@/messages/en';

const LoginForm = () => {
const searchParams = useSearchParams();
Expand Down Expand Up @@ -37,7 +37,7 @@ const LoginForm = () => {
);

return (
<div className="flex flex-col space-y-6 items-stretch">
<div className="flex flex-col space-y-6 items-stretch box-border">
{isSuccess ? (
<>
<div className="text-center flex justify-center">
Expand Down
27 changes: 18 additions & 9 deletions src/components/bookmark/BookmarksTeamList.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,31 @@
'use client';

import { TeamLinks } from '@/services/database/link';
import { Team } from '@prisma/client';
import { BsFillBookmarkFill } from '@react-icons/all-files/bs/BsFillBookmarkFill';
import NoContent from '../layout/NoContent';
import { BookmarkItem } from './BookmarkItem';
import Link from 'next/link';
import SearchInput from '../digests/SearchInput';
import { TeamLinks } from '@/services/database/link';
import NoContent from '../layout/NoContent';
import { BookmarkItem } from './BookmarkItem';
import CreateBookmarkButton from './CreateBookmarkButton';

type Props = {
teamLinks: TeamLinks;
teamId: string;
teamSlug: string;
team: Team;
};

export const BookmarksTeamList = ({ teamLinks, teamId, teamSlug }: Props) => {
export const BookmarksTeamList = ({ teamLinks, team }: Props) => {
return (
<div className="w-full">
<SearchInput className="mb-4" />
<div className="flex w-full justify-between items-center gap-4 mb-4">
<div className="flex-1 ">
<SearchInput />
</div>
<div className="flex">
<CreateBookmarkButton team={team} />
</div>
</div>

{teamLinks.length < 1 ? (
<NoContent
icon={<BsFillBookmarkFill />}
Expand All @@ -34,8 +43,8 @@ export const BookmarksTeamList = ({ teamLinks, teamId, teamSlug }: Props) => {
>
<BookmarkItem
teamLink={teamLink}
teamSlug={teamSlug}
teamId={teamId}
teamSlug={team.slug}
teamId={team.id}
digestId={
teamLink.bookmark?.find(
(teamLink) => teamLink?.digestBlocks?.length
Expand Down
Original file line number Diff line number Diff line change
@@ -1,35 +1,40 @@
'use client';

import { PlusIcon } from '@heroicons/react/24/solid';
import { Team } from '@prisma/client';
import { FaTelegramPlane } from '@react-icons/all-files/fa/FaTelegramPlane';
import { BookmarkModal } from './BookmarkModal';
import { Dialog, DialogContent, DialogTrigger } from '../Dialog';
import { useState } from 'react';
import Button from '../Button';
import { Dialog, DialogContent, DialogTrigger } from '../Dialog';
import { BookmarkModal } from './BookmarkModal';

type Props = { team: Team };
interface Props {
team: Team;
}

const BookmarkButton = ({ team }: Props) => {
export default function CreateBookmarkButton({ team }: Props) {
const [isDialogOpen, setIsDialogOpen] = useState(false);
return (
<Dialog open={isDialogOpen} onOpenChange={setIsDialogOpen}>
<DialogTrigger asChild>
<button
className="btn-add-link"
aria-label="Add bookmark"
title="Add bookmark"
<Button
onClick={() => setIsDialogOpen(true)}
variant="default"
size="md"
icon={<PlusIcon className="h-4 w-4" aria-hidden="true" />}
title="Create a new bookmark"
className="px-0"
>
<span className="hidden md:block">Add Bookmark</span>
<FaTelegramPlane />
</button>
New bookmark
</Button>
</DialogTrigger>
<DialogContent
containerClassName="w-full sm:max-w-md"
title="New Bookmark"
title="New bookmark"
description="Add a new link to your team feed"
closeIcon
>
<BookmarkModal onSuccess={() => setIsDialogOpen(false)} team={team} />
</DialogContent>
</Dialog>
);
};

export default BookmarkButton;
}
51 changes: 51 additions & 0 deletions src/components/bookmark/HeaderCreateBookmarkButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { PlusIcon } from '@heroicons/react/24/solid';
import { Team } from '@prisma/client';
import { useEffect, useState } from 'react';
import { Dialog, DialogContent, DialogTrigger } from '../Dialog';
import { BookmarkModal } from './BookmarkModal';

type Props = { team: Team };

const HeaderCreateBookmarkButton = ({ team }: Props) => {
const [isDialogOpen, setIsDialogOpen] = useState(false);

function onKeyDown(event: KeyboardEvent) {
if (event.key === 'b' && event.ctrlKey) {
setIsDialogOpen(true);
}
}

useEffect(() => {
window.addEventListener('keydown', onKeyDown);
return () => {
window.removeEventListener('keydown', onKeyDown);
};
}, []);

return (
<Dialog open={isDialogOpen} onOpenChange={setIsDialogOpen}>
<DialogTrigger asChild>
<button
className="btn-add-link"
aria-label="New bookmark"
title="New bookmark"
>
<span className="hidden md:block whitespace-nowrap">
New bookmark
</span>
<PlusIcon className="h-3.5 w-3.5" aria-hidden="true" />
</button>
</DialogTrigger>
<DialogContent
containerClassName="w-full sm:max-w-md"
title="New Bookmark"
description="Add a new link to your team feed"
closeIcon
>
<BookmarkModal onSuccess={() => setIsDialogOpen(false)} team={team} />
</DialogContent>
</Dialog>
);
};

export default HeaderCreateBookmarkButton;
8 changes: 4 additions & 4 deletions src/components/digests/SearchInput.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
'use client';

import { InputHTMLAttributes, useCallback, useTransition } from 'react';
import { MagnifyingGlassIcon } from '@heroicons/react/24/outline';
import { AiOutlineLoading3Quarters as LoadingIcon } from '@react-icons/all-files/ai/AiOutlineLoading3Quarters';
import { usePathname, useRouter, useSearchParams } from 'next/navigation';
import debounce from 'lodash/debounce';
import { usePathname, useRouter, useSearchParams } from 'next/navigation';
import { InputHTMLAttributes, useCallback, useTransition } from 'react';

const SearchInput = ({
className,
Expand All @@ -30,7 +30,7 @@ const SearchInput = ({

return (
<div className={className}>
<div className="mt-2 flex rounded-md shadow-sm">
<div className="flex rounded-md shadow-sm">
<div className="relative flex flex-grow items-stretch focus-within:z-10">
<div className="pointer-events-none absolute inset-y-0 left-0 flex items-center pl-3">
{isPending ? (
Expand All @@ -49,7 +49,7 @@ const SearchInput = ({
type="search"
name="search"
id="search"
className="block w-full rounded-md border-0 py-1.5 pl-10 text-gray-900 ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
className="block w-full rounded-md border-0 py-1.5 pl-10 text-gray-900 ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-violet-600 sm:text-sm sm:leading-6"
placeholder="Search bookmarks"
onInput={onSearch}
defaultValue={searchParams?.get('search') || ''}
Expand Down
22 changes: 11 additions & 11 deletions src/components/layout/NavMenu/NavMenu.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
'use client';

import { routes } from '@/core/constants';
import { Team } from '@prisma/client';
import { HiChevronDown } from '@react-icons/all-files/hi/HiChevronDown';
import * as NavigationMenu from '@radix-ui/react-navigation-menu';
import { usePathname } from 'next/navigation';
import BookmarkButton from '../../bookmark/BookmarkButton';
import { signOut } from 'next-auth/react';
import Divider from './Divider';
import Item from './Item';
import {
AdjustmentsVerticalIcon,
CheckIcon,
UserIcon,
ArrowLeftOnRectangleIcon,
CheckIcon,
PlusIcon,
UserIcon,
} from '@heroicons/react/24/solid';
import { Team } from '@prisma/client';
import * as NavigationMenu from '@radix-ui/react-navigation-menu';
import { HiChevronDown } from '@react-icons/all-files/hi/HiChevronDown';
import { signOut } from 'next-auth/react';
import { usePathname } from 'next/navigation';
import HeaderCreateBookmarkButton from '../../bookmark/HeaderCreateBookmarkButton';
import Divider from './Divider';
import Item from './Item';

type Props = {
teams?: Team[];
Expand All @@ -36,7 +36,7 @@ export const NavMenu = ({ teams }: Props) => {
<div>
{currentTeam && pathName !== '/' && (
<>
<BookmarkButton team={currentTeam} />
<HeaderCreateBookmarkButton team={currentTeam} />
</>
)}
</div>
Expand Down
30 changes: 19 additions & 11 deletions src/components/pages/DigestEditPage.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,21 @@
'use client';

import { useRouter } from 'next/navigation';
import { routes } from '@/core/constants';
import useCustomToast from '@/hooks/useCustomToast';
import useTransitionRefresh from '@/hooks/useTransitionRefresh';
import api from '@/lib/api';
import { useRouter } from 'next/navigation';

import useAddAndRemoveBlockOnDigest from '@/hooks/useAddAndRemoveBlockOnDigest';
import { ApiDigestResponseSuccess } from '@/pages/api/teams/[teamId]/digests';
import { getDigest } from '@/services/database/digest';
import { TeamLinksData } from '@/services/database/link';
import { getTeamBySlug } from '@/services/database/team';
import { reorderList } from '@/utils/actionOnList';
import { getRelativeDate } from '@/utils/date';
import { digestBlockToTemplateBlocks } from '@/utils/template';
import { EyeSlashIcon, RssIcon, TrashIcon } from '@heroicons/react/24/outline';
import { EyeIcon } from '@heroicons/react/24/solid';
import { DigestBlock, DigestBlockType } from '@prisma/client';
import { BsFillBookmarkFill } from '@react-icons/all-files/bs/BsFillBookmarkFill';
import { AxiosError, AxiosResponse } from 'axios';
Expand All @@ -26,22 +31,18 @@ import { useMutation } from 'react-query';
import Button from '../Button';
import { Input, TextArea } from '../Input';
import { DeletePopover } from '../Popover';
import { BlockListDnd } from '../digests/BlockListDnd';
import BookmarkListDnd from '../bookmark/BookmarkListDnd';
import CreateBookmarkButton from '../bookmark/CreateBookmarkButton';
import { BlockListDnd } from '../digests/BlockListDnd';
import SearchInput from '../digests/SearchInput';
import CreateTemplateModal from '../digests/templates/CreateTemplateModal';
import NoContent from '../layout/NoContent';
import SectionContainer from '../layout/SectionContainer';
import Pagination from '../list/Pagination';
import { Breadcrumb } from '../teams/Breadcrumb';
import DigestEditVisit from './DigestEditVisit';
import DigestEditTypefully from './DigestEditTypefully';
import DigestEditSendNewsletter from './DigestEditSendNewsletter';
import { EyeIcon } from '@heroicons/react/24/solid';
import CreateTemplateModal from '../digests/templates/CreateTemplateModal';
import { digestBlockToTemplateBlocks } from '@/utils/template';
import { TeamLinksData } from '@/services/database/link';
import { getDigest } from '@/services/database/digest';
import { getTeamBySlug } from '@/services/database/team';
import DigestEditTypefully from './DigestEditTypefully';
import DigestEditVisit from './DigestEditVisit';

type Props = {
teamLinksData: TeamLinksData;
Expand Down Expand Up @@ -285,7 +286,14 @@ export const DigestEditPage = ({
</div>

<SectionContainer title="Bookmarks" className="relative">
<SearchInput className="mb-4" />
<div className="flex w-full justify-between items-center gap-4 mb-4">
<div className="flex-1">
<SearchInput />
</div>
<div className="flex">
<CreateBookmarkButton team={team} />
</div>
</div>
<div className="flex flex-col gap-2">
{teamLinks && teamLinks.length > 0 ? (
<BookmarkListDnd
Expand Down
Loading

0 comments on commit 2c4b133

Please sign in to comment.