Skip to content

Commit

Permalink
Feature/Account module preparation
Browse files Browse the repository at this point in the history
  • Loading branch information
Ryczko committed Jan 15, 2024
1 parent 1968606 commit 3f364db
Show file tree
Hide file tree
Showing 19 changed files with 191 additions and 197 deletions.
4 changes: 3 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,10 @@ GOOGLE_SECRET=


# Feature toogles
NEXT_PUBLIC_PROFILE_SETTINGS=
NEXT_PUBLIC_REMOVE_ACCOUNT=
NEXT_PUBLIC_BLOCK_MULTIPLE_ANSWERS=
NEXT_PUBLIC_ALLOW_MULTIPLE_ANSWERS=



# Others
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

## 🌟 About FormsLab

<img width="1527" alt="formslab create form" src="https://github.com/Ryczko/FormsLab/assets/51440879/40b0d4be-84a6-42e4-92e5-f840400e5135">
<img width="1527" alt="formslab create form" src="https://github.com/Ryczko/FormsLab/assets/51440879/89dc029a-ff8a-4721-a7f3-70eb358c7249">
</br> </br>
<strong>FormsLab</strong> is an open source tool that can be used as a feedback app to collect feedback from customers, create polls for voting, or as a survey creator. Thanks to the ability to create any forms with many types of questions, nothing limits you, and your forms will always look unique 😍
</br> </br>
Expand Down
4 changes: 2 additions & 2 deletions i18n.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@ module.exports = {
'404',
'login',
'signup',
'settings',
'account',
'surveys',
'surveyAnswer',
'survey',
'thankyou'
'thankyou',
],
},
loadLocaleFrom: (lang, ns) =>
Expand Down
5 changes: 2 additions & 3 deletions locales/en/settings.json → locales/en/account.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
{
"title": "Settings",
"content": "Settings - FormsLab",
"heading": "Hi",
"title": "Account",
"content": "Account - FormsLab",
"deleteAccountButtonTitle": "Delete my account",
"deleteAccountButton": "Delete my account",
"dialogTitle": "Delete my account",
Expand Down
Binary file modified public/images/creator.webp
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useState } from 'react';
import { useApplicationContext } from 'features/application/context';

export const useSettingsManager = () => {
export const useAccountManager = () => {
const { error, user } = useApplicationContext();
const [isOpen, setIsOpen] = useState(false);
const [isRemoving] = useState(false);
Expand Down
2 changes: 1 addition & 1 deletion src/features/application/components/MainSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export default function MainSection({
alt={alt}
src={image}
width={500}
height={215}
height={265}
priority
className="rounded-md border shadow-sm"
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ export default function QuestionBlockWrapper({
<div className="flex w-full items-start gap-2">
<button
onClick={onExpand}
className="cursor-pointer rounded-md border border-opacity-100 bg-zinc-50 p-[13px]"
className="h-[42px] cursor-pointer rounded-md border border-opacity-100 bg-zinc-50 p-[13px]"
>
<ChevronDownIcon
className={clsx(
Expand All @@ -99,7 +99,7 @@ export default function QuestionBlockWrapper({
onInput={handleQuestionChange}
value={questionTitle}
error={questionError()}
className="mt-0"
className="mt-0 h-[42px]"
maxLength={MAX_QUESTION_LENGTH}
data-test-id={`question-input-${index}`}
/>
Expand All @@ -115,7 +115,7 @@ export default function QuestionBlockWrapper({
<div className="flex gap-2">
{isDraggingPossible && (
<div
className="cursor-pointer rounded-md border bg-zinc-50 p-[13px] shadow-sm hover:scale-95"
className="h-[42px] cursor-pointer rounded-md border bg-zinc-50 p-[13px] shadow-sm hover:scale-95"
{...dragHandleProps}
>
<SelectorIcon className="w-[15px]" />
Expand All @@ -126,7 +126,7 @@ export default function QuestionBlockWrapper({
onClick={removeQuestion}
disabled={isEditMode}
data-test-id={`remove-question-${index}`}
className="cursor-pointer rounded-md border border-red-200 bg-red-200 p-[13px] shadow-sm hover:scale-95 disabled:cursor-not-allowed disabled:opacity-40 disabled:hover:scale-100"
className="h-[42px] cursor-pointer rounded-md border border-red-200 bg-red-200 p-[13px] shadow-sm hover:scale-95 disabled:cursor-not-allowed disabled:opacity-40 disabled:hover:scale-100"
>
<TrashIcon className="w-[15px] text-red-900" />
</button>
Expand Down
2 changes: 1 addition & 1 deletion src/features/surveys/managers/surveyAnswerManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ export const useSurveyAnswerManager = (initialData: SurveyWithQuestions) => {
setFormData(initialData);
}
if (
process.env.NEXT_PUBLIC_BLOCK_MULTIPLE_ANSWERS &&
!process.env.NEXT_PUBLIC_ALLOW_MULTIPLE_ANSWERS &&
localStorageValue.includes(surveyId) &&
!isAnswering
) {
Expand Down
70 changes: 29 additions & 41 deletions src/layout/Navigation/Navigation.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
import { CogIcon, LogoutIcon, MenuIcon } from '@heroicons/react/outline';
import { LogoutIcon, MenuIcon, UserIcon } from '@heroicons/react/outline';
import { Fragment, useState } from 'react';
import { Menu, Transition } from '@headlessui/react';
import Image from 'next/image';
import Logo from 'layout/Logo/Logo';
import ButtonLink from 'shared/components/ButtonLink/ButtonLink';
import BurgerMenu from 'layout/BurgerMenu/BurgerMenu';
import Button, { ButtonVariant } from 'shared/components/Button/Button';
import AvatarIcon from '../../../public/images/avatar.svg';
import Button, {
ButtonSize,
ButtonVariant,
} from 'shared/components/Button/Button';
import GithubCorner from 'layout/GithubCorner/GithubCorner';
import { useApplicationContext } from 'features/application/context';
import IconButtonLink from 'shared/components/IconButtonLink/IconButtonLink';
import useTranslation from 'next-translate/useTranslation';
import { signOut } from 'next-auth/react';
import Avatar from 'shared/components/Avatar/Avatar';
import ButtonLink from 'shared/components/ButtonLink/ButtonLink';

function Navigation() {
const { user, loading } = useApplicationContext();
Expand Down Expand Up @@ -57,23 +58,7 @@ function Navigation() {
<p className="ml-2 mr-4 hidden items-center truncate sm:block">
{user.name}
</p>
{user.image ? (
<Image
src={user.image}
alt="user photo"
width={32}
height={32}
className="rounded-full"
/>
) : (
<Image
src={AvatarIcon}
alt="user photo"
width={32}
height={32}
className="rounded-full"
/>
)}
<Avatar src={user.image} />
</Menu.Button>
<Transition
as={Fragment}
Expand All @@ -84,24 +69,27 @@ function Navigation() {
leaveFrom="transform opacity-100 scale-100"
leaveTo="transform opacity-0 scale-95"
>
<Menu.Items className="absolute right-0 mt-2 origin-top-right divide-y divide-zinc-100 rounded-md bg-white shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none">
<div className="flex flex-col justify-end p-1">
{process.env.NEXT_PUBLIC_REMOVE_ACCOUNT && (
<Menu.Items className="absolute right-0 mt-2 min-w-[160px] origin-top-right divide-y divide-zinc-100 rounded-md bg-white shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none">
<div className="p-1">
{process.env.NEXT_PUBLIC_PROFILE_SETTINGS && (
<Menu.Item>
<IconButtonLink
<ButtonLink
variant={ButtonVariant.FLAT}
href={'/settings'}
icon={<CogIcon className="h-5 w-5" />}
sizeType={ButtonSize.FULL}
href={'/account'}
icon={<UserIcon className="h-5 w-5" />}
>
{t('navigation.settingsButton')}
</IconButtonLink>
<span className="ms-1">Account</span>
</ButtonLink>
</Menu.Item>
)}

<Menu.Item>
<Button
onClick={logout}
sizeType={ButtonSize.FULL}
variant={ButtonVariant.FLAT}
className="w-40 text-red-600 hover:bg-red-100"
className="text-red-600 hover:bg-red-100"
icon={<LogoutIcon className="h-5 w-5" />}
>
{t('navigation.signOutButton')}
Expand Down Expand Up @@ -132,28 +120,28 @@ function Navigation() {
href={'/survey/create'}
onClick={() => setIsOpen(!isOpen)}
variant={ButtonVariant.FLAT}
className="mb-3 w-[95%] lg:w-auto"
className="mb-2 w-[95%] lg:w-auto"
>
{t('navigation.createSurveyButton')}
</ButtonLink>
<ButtonLink
href={'/surveys'}
className="mb-3 w-[95%] lg:w-auto"
onClick={() => setIsOpen(!isOpen)}
className="mb-2 w-[95%] lg:w-auto"
variant={ButtonVariant.FLAT}
>
{t('navigation.mySurveysButton')}
</ButtonLink>
{process.env.NEXT_PUBLIC_REMOVE_ACCOUNT && (
<IconButtonLink
className="mb-3 w-[95%] justify-center lg:w-auto"
href="/settings"
{process.env.NEXT_PUBLIC_PROFILE_SETTINGS && (
<Button
className="mb-2 w-[95%] justify-center lg:w-auto"
href={'/account'}
onClick={() => setIsOpen(!isOpen)}
variant={ButtonVariant.FLAT}
icon={<CogIcon className="h-5 w-5" />}
icon={<UserIcon className="h-5 w-5" />}
>
{t('navigation.settingsButton')}
</IconButtonLink>
Account
</Button>
)}
<Button
onClick={logout}
Expand Down
111 changes: 111 additions & 0 deletions src/pages/account/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import Head from 'next/head';
import { TrashIcon } from '@heroicons/react/outline';
import withAnimation from 'shared/HOC/withAnimation';
import Header from 'shared/components/Header/Header';
import withProtectedRoute from 'shared/HOC/withProtectedRoute';

import withFeatureToggles from 'shared/HOC/withFeatureToggles';
import useTranslation from 'next-translate/useTranslation';
import { useAccountManager } from 'features/account/accountManager';
import Button, { ButtonVariant } from 'shared/components/Button/Button';
import StyledDialog from 'shared/components/StyledDialog/StyledDialog';
import Avatar from 'shared/components/Avatar/Avatar';
import { formatDateDistance } from 'shared/utilities/convertTime';

function AccountPage() {
const { t } = useTranslation('account');

const {
user,
isOpen,
openDeleteModal,
closeDeleteModal,
handleOnAccountDelete,
isRemoving,
} = useAccountManager();

return (
<>
<Head>
<title>{t('title')}</title>
<meta name="description" content={t('content')} />
</Head>

<Header>Your account</Header>

{user ? (
<div className="mt-4 flex w-full items-center justify-center gap-12 text-left">
<Avatar size={160} src={user.image} classNames="border" />

<div className="mb-4">
<h2 className="mb-2 text-2xl">{user?.name}</h2>
<p>{user?.email}</p>
<p>Account created: {formatDateDistance(user.createdAt)}</p>
</div>
</div>
) : (
<></>
)}

{process.env.NEXT_PUBLIC_REMOVE_ACCOUNT && (
<>
<div className="flex flex-col items-center justify-center space-y-2">
<div className="flex w-full md:ml-2 md:w-auto">
<Button
variant={ButtonVariant.DANGER}
title={t('deleteAccountButtonTitle')}
className="ml-2 mt-2 w-full justify-center px-3 sm:mt-0 md:w-auto"
onClick={openDeleteModal}
icon={<TrashIcon className="h-5 w-5" />}
>
{t('deleteAccountButton')}
</Button>
</div>
</div>
<StyledDialog
isOpen={isOpen}
onClose={closeDeleteModal}
title={t('dialogTitle')}
content={
<>
<div className="mt-2">
<p className="text-sm text-red-500">
{t('dialogContentFirst')}&nbsp;
<span className="font-bold">
{t('dialogContentSecond')}
</span>{' '}
{t('dialogContentThird')}
</p>
</div>
<div className="mt-6 flex justify-between space-x-3">
<Button
variant={ButtonVariant.SECONDARY}
onClick={closeDeleteModal}
className="uppercase"
disabled={isRemoving}
>
{t('buttonCancle')}
</Button>
<Button
variant={ButtonVariant.DANGER}
onClick={handleOnAccountDelete}
icon={<TrashIcon className="h-5 w-5" />}
className="uppercase"
isLoading={isRemoving}
>
{t('buttonConfirm')}
</Button>
</div>
</>
}
/>
</>
)}
</>
);
}

export default withFeatureToggles(
withProtectedRoute(withAnimation(AccountPage)),
[process.env.NEXT_PUBLIC_PROFILE_SETTINGS]
);
Loading

1 comment on commit 3f364db

@vercel
Copy link

@vercel vercel bot commented on 3f364db Jan 15, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

formslab – ./

formslab-ryczko.vercel.app
formslab-git-main-ryczko.vercel.app
formslab.vercel.app

Please sign in to comment.