RC-10: create shared components for history and favorites pages

This commit is contained in:
2023-08-27 11:56:36 +01:00
parent e6912e2541
commit a5b30367cb
15 changed files with 467 additions and 149 deletions

View File

@@ -9,7 +9,7 @@ function AboutUs({ t }) {
<div className="flex gap-64 mb-[126px]">
<div className="flex items-center">
<iframe
className="rounded-[20px]"
className="rounded-20"
width="715"
height="402"
src="https://www.youtube.com/embed/rNSIwjmynYQ?controls=0"
@@ -18,7 +18,7 @@ function AboutUs({ t }) {
allowFullScreen
/>
</div>
<aside className="flex flex-col items-center py-40 px-52 bg-primary-light rounded-[20px]">
<aside className="flex flex-col items-center py-40 px-52 bg-primary-light rounded-20">
<h3 className="mb-16 text-lg text-common-layout font-medium">{t('about_us_subject')}</h3>
<p className="mb-16 text-lg text-common-layout font-light">{t('about_us_text_1')}</p>
<p className="mb-16 text-lg text-common-layout font-light">{t('about_us_text_2')}</p>

View File

@@ -4,10 +4,10 @@ import { Link } from 'react-router-dom';
function ArticleCard({ t, id, title, description, image, updated }) {
return (
<article className="flex flex-col justify-between max-w-[460px] w-full h-[526px] bg-primary-light rounded-[20px] shadow-light">
<article className="flex flex-col justify-between max-w-[460px] w-full h-[526px] bg-primary-light rounded-20 shadow-light">
<div>
<img
className="w-full h-[230px] mb-20 rounded-[20px] object-cover"
className="w-full h-[230px] mb-20 rounded-20 object-cover"
src={image}
alt={title}
width="460"

View File

@@ -58,7 +58,7 @@ function FeedbackForm({ t }) {
<form
name="signinForm"
noValidate
className="grid grid-cols-2 gap-x-20 gap-y-32 px-80 py-40 bg-primary-light rounded-[20px]"
className="grid grid-cols-2 gap-x-20 gap-y-32 px-80 py-40 bg-primary-light rounded-20"
onSubmit={handleSubmit(onSubmit)}
>
<legend className="col-span-2 justify-self-center max-w-[860px] mb-8 text-4xl font-medium text-center">

View File

@@ -2,7 +2,7 @@ import { memo } from 'react';
function StatisticsCard({ title, text }) {
return (
<article className="flex flex-col justify-start items-center max-w-[460px] w-full min-h-[356px] h-full pt-32 px-40 text-common-primary bg-primary-light rounded-[20px] shadow-light even:bg-secondary-main even:text-primary-light">
<article className="flex flex-col justify-start items-center max-w-[460px] w-full min-h-[356px] h-full pt-32 px-40 text-common-primary bg-primary-light rounded-20 shadow-light even:bg-secondary-main even:text-primary-light">
<h3 className="mb-52 text-[80px] font-semibold">{title}</h3>
<p className="text-xl leading-5 font-light">{text}</p>
</article>

View File

@@ -104,6 +104,7 @@ function ProfilePage({ t }) {
});
const { dirtyFields, errors } = formState;
// eslint-disable-next-line consistent-return
const uploadPicture = async (event) => {
const { target } = event;
if (target.files && target.files[0]) {

View File

@@ -0,0 +1,17 @@
import FuseSvgIcon from '@fuse/core/FuseSvgIcon';
import Typography from '@mui/material/Typography';
import clsx from 'clsx';
import { memo } from 'react';
function DateMark({ className, update }) {
return (
<span className={clsx('flex justify-center items-center gap-10', className)}>
<FuseSvgIcon>heroicons-outline:calendar</FuseSvgIcon>
<Typography variant="body1" className="text-lg font-medium text-common-secondary">
{update}
</Typography>
</span>
);
}
export default memo(DateMark);

View File

@@ -0,0 +1,21 @@
import FuseSvgIcon from '@fuse/core/FuseSvgIcon';
import clsx from 'clsx';
import { memo } from 'react';
function UpdateMark({ className, favorite, id, onClick }) {
const hasCallback = typeof onClick !== 'undefined';
return (
<button
className="w-[24px] h-[24px] cursor-pointer"
type="button"
onClick={() => hasCallback && onClick(id)}
>
<FuseSvgIcon className={clsx('w-full h-full', className, favorite && 'text-secondary-main')}>
heroicons-outline:heart
</FuseSvgIcon>
</button>
);
}
export default memo(UpdateMark);

View File

@@ -0,0 +1,25 @@
import Typography from '@mui/material/Typography';
import _ from '@lodash';
import clsx from 'clsx';
import { memo, useMemo } from 'react';
function MetaMark({ className, category, status }) {
const text = useMemo(
() => (status ? `Status: ${_.startCase(status)}` : _.startCase(category)),
[category, status]
);
return (
<Typography
variant="body1"
className={clsx(
'flex justify-center align-center px-20 py-2 font-medium border-2 rounded-8',
className
)}
>
{text}
</Typography>
);
}
export default memo(MetaMark);

View File

@@ -0,0 +1,56 @@
import FuseSvgIcon from '@fuse/core/FuseSvgIcon';
import Typography from '@mui/material/Typography';
import _ from '@lodash';
import clsx from 'clsx';
import { memo } from 'react';
function PropertiesHeader({ className, categories, layouts, onCategory, onLayout }) {
return (
<div
className={clsx(
'flex items-center gap-44 w-full py-9 px-52 rounded-20 bg-white shadow-light',
className
)}
>
<div className="grow flex items-center justify-start gap-16 py-16 border-r-1 border-common-disabled">
{categories.map(({ name, amount, active }, idx) => (
<button
key={name + idx}
type="button"
className={clsx(
'text-2xl text-common-layout cursor-pointer',
active && 'text-secondary-main font-semibold cursor-default'
)}
onClick={() => onCategory(name)}
>{`${_.startCase(name)} (${amount})`}</button>
))}
</div>
<div className="flex items-center gap-60 py-16">
{layouts.map(({ name, active }, idx) => (
<button
key={name + idx}
type="button"
className={clsx(
'flex justify-center items-center gap-10 cursor-pointer',
active && '!cursor-default'
)}
onClick={() => onLayout(name)}
>
<FuseSvgIcon className={clsx('text-common-secondary', active && 'text-secondary-main')}>
{`heroicons-outline:view-${name}`}
</FuseSvgIcon>
<Typography
variant="body1"
className={clsx('text-2xl text-common-secondary', active && 'text-secondary-main')}
>
{_.startCase(name)}
</Typography>
</button>
))}
</div>
</div>
);
}
export default memo(PropertiesHeader);

View File

@@ -0,0 +1,77 @@
import FuseSvgIcon from '@fuse/core/FuseSvgIcon';
import Typography from '@mui/material/Typography';
import { memo } from 'react';
import { Link } from 'react-router-dom';
import DateMark from './DateMark';
import FavoriteButton from './FavoriteButton';
import MetaMark from './MetaMark';
import StatisticsValue from './StatisticsValue';
function PropertyGridCard({
id,
image,
title,
category,
status,
update,
favorite,
statistics,
onFavorite,
onDelete,
}) {
return (
<article className="w-[470px] px-20 pt-20 rounded-20 bg-white shadow-light overflow-hidden">
<div className="flex justify-between mb-[25px]">
<div className="flex gap-10">
<MetaMark
category={category}
className="text-common-highlight1 border-common-highlight1"
/>
<MetaMark status={status} className="text-common-highlight2 border-common-highlight2" />
</div>
<div className="flex gap-20">
<FavoriteButton favorite={favorite} id={id} onClick={onFavorite} />
<DateMark update={update} />
</div>
</div>
<div className="flex flex-col items-start mb-[29px]">
<Typography variant="h3" className="mb-[17px] text-3xl font-semibold">
{title}
</Typography>
<img src={image} alt={title} className="w-full h-160 rounded-3xl object-cover" />
<div className="grid grid-cols-2 justify-between w-full">
{statistics.map(({ subject, value, mode }, idx) => (
<StatisticsValue
key={subject + value + idx}
subject={subject}
value={value}
mode={mode}
/>
))}
</div>
</div>
<div className="flex w-[calc(100%+40px)] -mx-20 border-t-1 border-common-disabled">
<button
className="flex justify-center items-center gap-10 w-full py-20 border-r-1 border-common-disabled cursor-pointer"
type="button"
onClick={() => onDelete(id)}
>
<FuseSvgIcon className="text-common-secondary">heroicons-outline:trash</FuseSvgIcon>
<Typography variant="body1" className="text-common-secondary font-medium">
Delete
</Typography>
</button>
<Link
className="flex justify-center items-center w-full py-[22px] text-lg font-semibold text-secondary-main border-l-1 border-white cursor-pointer"
to={`/property/${id}`}
>
Details
</Link>
</div>
</article>
);
}
export default memo(PropertyGridCard);

View File

@@ -0,0 +1,83 @@
import FuseSvgIcon from '@fuse/core/FuseSvgIcon';
import Typography from '@mui/material/Typography';
import { memo } from 'react';
import { Link } from 'react-router-dom';
import DateMark from './DateMark';
import FavoriteButton from './FavoriteButton';
import MetaMark from './MetaMark';
import StatisticsValue from './StatisticsValue';
function PropertyListItem({
id,
image,
title,
category,
status,
update,
favorite,
statistics,
onFavorite,
onDelete,
}) {
return (
<article className="flex w-full p-20 rounded-20 bg-white shadow-light overflow-hidden">
<img
src={image}
alt={title}
className="w-[80px] h-[80px] mr-[15px] rounded-3xl object-cover"
/>
<div className="mr-20">
<div className="flex justify-start gap-60 mb-[22px]">
<div className="flex gap-10">
<MetaMark
category={category}
className="text-common-highlight1 border-common-highlight1"
/>
<MetaMark status={status} className="text-common-highlight2 border-common-highlight2" />
</div>
<div className="flex gap-20">
<FavoriteButton favorite={favorite} id={id} onClick={onFavorite} />
<DateMark update={update} />
</div>
</div>
<Typography variant="h3" className="max-w-[480px] text-3xl font-semibold truncate">
{title}
</Typography>
</div>
<div className="grow flex mr-20">
{statistics.map(
({ subject, value, mode }, idx) =>
(idx === 0 || idx === 1) && (
<StatisticsValue
key={subject + value + idx}
subject={subject}
value={value}
mode={mode}
className="-my-[6px]"
/>
)
)}
</div>
<div className="flex flex-col justify-between gap-16">
<button
className="self-end flex justify-center items-center gap-10 cursor-pointer"
type="button"
onClick={() => onDelete(id)}
>
<FuseSvgIcon className="text-common-secondary">heroicons-outline:trash</FuseSvgIcon>
</button>
<Link
className="flex justify-center items-center px-[53px] py-20 -mr-20 -mb-20 text-lg font-semibold text-secondary-main border-l-1 border-t-1 rounded-tl-[20px] border-common-disabled cursor-pointer"
to={`/property/${id}`}
>
Details
</Link>
</div>
</article>
);
}
export default memo(PropertyListItem);

View File

@@ -0,0 +1,38 @@
import Typography from '@mui/material/Typography';
import { STATISTICS_MODES } from 'app/configs/consts';
import clsx from 'clsx';
import { memo } from 'react';
function StatisticsValue({ className, subject, value, mode }) {
const isPositive = mode === STATISTICS_MODES.positive;
const isExtraPositive = mode === STATISTICS_MODES.extra_positive;
const isNegative = mode === STATISTICS_MODES.negative;
const isExtraNegative = mode === STATISTICS_MODES.extra_negative;
return (
<span
className={clsx(
'max-w-[210px] w-full py-[15px] pl-20 text-left rounded-xl',
className,
isExtraPositive && 'bg-accept-light',
isExtraNegative && 'bg-error-light'
)}
>
<Typography variant="body1" className="text-lg text-left leading-tight">
{subject}
</Typography>
<Typography
variant="h4"
className={clsx(
'text-[28px] font-semibold text-left leading-tight',
(isPositive || isExtraPositive) && 'text-accept-main',
(isNegative || isExtraNegative) && 'text-error-main'
)}
>
{value}
</Typography>
</span>
);
}
export default memo(StatisticsValue);

View File

@@ -2,45 +2,46 @@
/* Print
/*----------------------------------------------------------------*/
@media all {
/* Never show page breaks in normal view */
.page-break-after,
.page-break-before {
display: none;
}
/* Never show page breaks in normal view */
.page-break-after,
.page-break-before {
display: none;
}
}
@media print {
/* html and body tweaks */
html, body {
height: auto !important;
overflow: initial !important;
background: none
}
/* html and body tweaks */
html,
body {
height: auto !important;
overflow: initial !important;
background: none;
}
/* Page breaks */
.page-break-after {
display: block;
page-break-after: always;
position: relative;
}
/* Page breaks */
.page-break-after {
display: block;
page-break-after: always;
position: relative;
}
.page-break-before {
display: block;
page-break-before: always;
position: relative;
}
.page-break-before {
display: block;
page-break-before: always;
position: relative;
}
/* General styles */
#fuse-toolbar,
#fuse-footer,
#fuse-navbar,
#fuse-settings-presets,
#fuse-layout .ps > .ps__rail-x,
#fuse-layout .ps > .ps__rail-y {
display: none !important;
}
/* General styles */
#fuse-toolbar,
#fuse-footer,
#fuse-navbar,
#fuse-settings-presets,
#fuse-layout .ps > .ps__rail-x,
#fuse-layout .ps > .ps__rail-y {
display: none !important;
}
#fuse-layout .ps {
overflow: visible !important;
}
#fuse-layout .ps {
overflow: visible !important;
}
}

View File

@@ -1,212 +1,211 @@
code[class*="language-"],
pre[class*="language-"] {
text-align: left;
white-space: pre-wrap;
word-break: break-all;
word-wrap: break-word;
color: #c3cee3;
background: #263238;
font-family: Roboto Mono,"Liberation Mono",Menlo,Courier,monospace;
font-size: 1em;
line-height: 1.5;
-moz-tab-size: 4;
-o-tab-size: 4;
tab-size: 4;
-webkit-hyphens: none;
-moz-hyphens: none;
-ms-hyphens: none;
hyphens: none;
code[class*='language-'],
pre[class*='language-'] {
text-align: left;
white-space: pre-wrap;
word-break: break-all;
word-wrap: break-word;
color: #c3cee3;
background: #263238;
font-family: Roboto Mono, 'Liberation Mono', Menlo, Courier, monospace;
font-size: 1em;
line-height: 1.5;
-moz-tab-size: 4;
-o-tab-size: 4;
tab-size: 4;
-webkit-hyphens: none;
-moz-hyphens: none;
-ms-hyphens: none;
hyphens: none;
}
code[class*="language-"]::-moz-selection,
pre[class*="language-"]::-moz-selection,
code[class*="language-"] ::-moz-selection,
pre[class*="language-"] ::-moz-selection {
background: #000000;
code[class*='language-']::-moz-selection,
pre[class*='language-']::-moz-selection,
code[class*='language-'] ::-moz-selection,
pre[class*='language-'] ::-moz-selection {
background: #000000;
}
code[class*="language-"]::selection,
pre[class*="language-"]::selection,
code[class*="language-"] ::selection,
pre[class*="language-"] ::selection {
background: #000000;
code[class*='language-']::selection,
pre[class*='language-']::selection,
code[class*='language-'] ::selection,
pre[class*='language-'] ::selection {
background: #000000;
}
:not(pre) > code[class*="language-"] {
white-space: normal;
border-radius: 0.2em;
padding: 0.1em;
:not(pre) > code[class*='language-'] {
white-space: normal;
border-radius: 0.2em;
padding: 0.1em;
}
pre[class*="language-"] {
overflow: auto;
position: relative;
padding: 12px;
border-radius: 4px;;
pre[class*='language-'] {
overflow: auto;
position: relative;
padding: 12px;
border-radius: 4px;
}
.language-css > code,
.language-sass > code,
.language-scss > code {
color: #fd9170;
color: #fd9170;
}
[class*="language-"] .namespace {
opacity: 0.7;
[class*='language-'] .namespace {
opacity: 0.7;
}
.token.plain-text {
color: #c3cee3;
color: #c3cee3;
}
.token.atrule {
color: #c792ea;
color: #c792ea;
}
.token.attr-name {
color: #ffcb6b;
color: #ffcb6b;
}
.token.attr-value {
color: #c3e88d;
color: #c3e88d;
}
.token.attribute {
color: #c3e88d;
color: #c3e88d;
}
.token.boolean {
color: #c792ea;
color: #c792ea;
}
.token.builtin {
color: #ffcb6b;
color: #ffcb6b;
}
.token.cdata {
color: #80cbc4;
color: #80cbc4;
}
.token.char {
color: #80cbc4;
color: #80cbc4;
}
.token.class {
color: #ffcb6b;
color: #ffcb6b;
}
.token.class-name {
color: #82aaff;
color: #82aaff;
}
.token.color {
color: #f2ff00;
color: #f2ff00;
}
.token.comment {
color: #546e7a;
color: #546e7a;
}
.token.constant {
color: #c792ea;
color: #c792ea;
}
.token.deleted {
color: #f07178;
color: #f07178;
}
.token.doctype {
color: #546e7a;
color: #546e7a;
}
.token.entity {
color: #f07178;
color: #f07178;
}
.token.function {
color: #c792ea;
color: #c792ea;
}
.token.hexcode {
color: #f2ff00;
color: #f2ff00;
}
.token.id {
color: #c792ea;
font-weight: bold;
color: #c792ea;
font-weight: bold;
}
.token.important {
color: #c792ea;
font-weight: bold;
color: #c792ea;
font-weight: bold;
}
.token.inserted {
color: #80cbc4;
color: #80cbc4;
}
.token.keyword {
color: #c792ea;
font-style: italic;
color: #c792ea;
font-style: italic;
}
.token.number {
color: #fd9170;
color: #fd9170;
}
.token.operator {
color: #89ddff;
color: #89ddff;
}
.token.prolog {
color: #546e7a;
color: #546e7a;
}
.token.property {
color: #80cbc4;
color: #80cbc4;
}
.token.pseudo-class {
color: #c3e88d;
color: #c3e88d;
}
.token.pseudo-element {
color: #c3e88d;
color: #c3e88d;
}
.token.punctuation {
color: #89ddff;
color: #89ddff;
}
.token.regex {
color: #f2ff00;
color: #f2ff00;
}
.token.selector {
color: #f07178;
color: #f07178;
}
.token.string {
color: #c3e88d;
color: #c3e88d;
}
.token.symbol {
color: #c792ea;
color: #c792ea;
}
.token.tag {
color: #f07178;
color: #f07178;
}
.token.unit {
color: #f07178;
color: #f07178;
}
.token.url {
color: #fd9170;
color: #fd9170;
}
.token.variable {
color: #f07178;
color: #f07178;
}

View File

@@ -2,68 +2,68 @@
Basic Table Styles
*/
.table-responsive {
display: block;
width: 100%;
overflow-x: auto;
-webkit-overflow-scrolling: touch;
-ms-overflow-style: -ms-autohiding-scrollbar;
display: block;
width: 100%;
overflow-x: auto;
-webkit-overflow-scrolling: touch;
-ms-overflow-style: -ms-autohiding-scrollbar;
}
table.simple {
width: 100%;
border: none;
border-spacing: 0;
text-align: left;
width: 100%;
border: none;
border-spacing: 0;
text-align: left;
}
table.simple thead tr th {
padding: 16px 8px;
font-weight: 500;
border-bottom: 1px solid rgba(0, 0, 0, 0.12);
white-space: nowrap;
padding: 16px 8px;
font-weight: 500;
border-bottom: 1px solid rgba(0, 0, 0, 0.12);
white-space: nowrap;
}
table.simple thead tr th:first-child {
padding-left: 24px;
padding-left: 24px;
}
table.simple thead tr th:last-child {
padding-right: 24px;
padding-right: 24px;
}
table.simple tbody tr td {
padding: 12px 8px;
border-bottom: 1px solid rgba(0, 0, 0, 0.12);
padding: 12px 8px;
border-bottom: 1px solid rgba(0, 0, 0, 0.12);
}
table.simple tbody tr td:first-child {
padding-left: 24px;
padding-left: 24px;
}
table.simple tbody tr td:last-child {
padding-right: 24px;
padding-right: 24px;
}
table.simple tbody tr:last-child td {
border-bottom: none;
border-bottom: none;
}
table.simple.clickable tbody tr {
cursor: pointer;
cursor: pointer;
}
table.simple.clickable tbody tr:hover {
background: rgba(0, 0, 0, 0.03);
background: rgba(0, 0, 0, 0.03);
}
table.simple.borderless {
border: none;
border: none;
}
table.simple.borderless tbody tr td{
border: none;
table.simple.borderless tbody tr td {
border: none;
}
table.simple.borderless thead tr th{
border: none;
table.simple.borderless thead tr th {
border: none;
}