mirror of
https://github.com/aljazceru/landscape-template.git
synced 2026-02-18 13:04:37 +01:00
updates: tagging project QA, general QA
This commit is contained in:
@@ -34,10 +34,10 @@ export default function CoverImageInput(props: Props) {
|
||||
wrapperClass='h-full'
|
||||
render={({ img, isUploading, isDraggingOnWindow }) =>
|
||||
<div className="w-full h-full group relative ">
|
||||
{!img && <div className={`w-full h-full flex flex-col justify-center items-center bg-gray-500 outline outline-2 outline-gray-200 ${props.rounded ?? 'rounded-12'}`}>
|
||||
<p className="text-center text-gray-100 text-body1 md:text-h1 mb-8"><FaImage /></p>
|
||||
<div className={`text-gray-100 text-center text-body4`}>
|
||||
Drop a <span className="font-bold">COVER IMAGE</span> here or <br /> <span className="text-blue-300 underline">Click to browse</span>
|
||||
{!img && <div className={`w-full h-full flex flex-col justify-center items-center bg-gray-100 border-dashed border-2 border-gray-200 ${props.rounded ?? 'rounded-12'}`}>
|
||||
<p className="text-center text-gray-800 text-body1 md:text-h1 mb-8"><FaImage /></p>
|
||||
<div className={`text-gray-700 text-center text-body4`}>
|
||||
Drop a <span className="font-bold">COVER IMAGE</span> here or <br /> <span className="text-blue-400 underline">Click to browse</span>
|
||||
</div>
|
||||
</div>}
|
||||
{img && <>
|
||||
@@ -45,10 +45,10 @@ export default function CoverImageInput(props: Props) {
|
||||
{!isUploading &&
|
||||
<div className="flex flex-wrap gap-16 absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 ">
|
||||
|
||||
<button type='button' className='py-8 px-16 rounded-12 bg-black bg-opacity-70 opacity-0 group-hover:opacity-100 hover:bg-opacity-90 transition-opacity text-white text-h1'>
|
||||
<button type='button' className='w-42 h-42 flex justify-center items-center rounded-full bg-gray-800 bg-opacity-60 opacity-0 group-hover:opacity-100 hover:bg-opacity-90 transition-opacity text-white text-body3'>
|
||||
<CgArrowsExchangeV />
|
||||
</button>
|
||||
<button type='button' className='py-8 px-16 rounded-12 bg-black bg-opacity-70 opacity-0 group-hover:opacity-100 hover:bg-opacity-90 transition-opacity text-white text-h1' onClick={(e) => { e.stopPropagation(); props.onChange(null) }}>
|
||||
<button type='button' className='w-42 h-42 flex justify-center items-center rounded-full bg-gray-800 bg-opacity-60 opacity-0 group-hover:opacity-100 hover:bg-opacity-90 transition-opacity text-white text-body3' onClick={(e) => { e.stopPropagation(); props.onChange(null) }}>
|
||||
<IoMdClose />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -5,7 +5,7 @@ import Card from "src/Components/Card/Card"
|
||||
export default function PostCardSkeleton() {
|
||||
return <div>
|
||||
<div className="flex gap-8 items-center mb-8">
|
||||
<Skeleton circle width={24} height={24} />
|
||||
<Skeleton circle width={32} height={32} />
|
||||
<span className='flex gap-4 mt-4'>
|
||||
<p className="text-gray-900 text-body5 font-medium"><Skeleton width="12ch" /></p>
|
||||
</span>
|
||||
|
||||
@@ -17,10 +17,12 @@ export default function PostCardHeader(props: Props) {
|
||||
|
||||
|
||||
const dateToShow = () => {
|
||||
const passedTime = dayjs().diff(props.date, 'hour');
|
||||
if (passedTime === 0) return 'now';
|
||||
if (passedTime < 24) return `${dayjs().diff(props.date, 'hour')}h ago`
|
||||
return dayjs(props.date).format('MMMM DD');
|
||||
const passedTimeHrs = dayjs().diff(props.date, 'hour');
|
||||
const passedTimesDays = Math.ceil(passedTimeHrs / 24);
|
||||
if (passedTimeHrs === 0) return 'now';
|
||||
if (passedTimeHrs < 24) return `${dayjs().diff(props.date, 'hour')}h ago`
|
||||
if (passedTimesDays < 29) return `${passedTimesDays} days`
|
||||
return dayjs(props.date).format('DD MMM');
|
||||
}
|
||||
|
||||
if (!props.author) return null
|
||||
@@ -29,10 +31,10 @@ export default function PostCardHeader(props: Props) {
|
||||
<div className="flex gap-8 items-center mb-8">
|
||||
<span className='flex'>
|
||||
<Link to={createRoute({ type: 'profile', id: props.author.id, username: props.author.name })}>
|
||||
<Avatar width={24} src={props.author.avatar} />
|
||||
<Avatar width={32} src={props.author.avatar} />
|
||||
</Link>
|
||||
{props.project && <Link className='-ml-12' to={createRoute({ type: "project", tag: props.project.hashtag })}>
|
||||
<Avatar src={props.project.thumbnail_image} width={24} />
|
||||
<Avatar src={props.project.thumbnail_image} width={32} />
|
||||
</Link>}
|
||||
</span>
|
||||
<span className='flex gap-4'>
|
||||
|
||||
@@ -34,7 +34,7 @@ export default function TagProjectInput({
|
||||
|
||||
const query = useMyProjectsQuery()
|
||||
|
||||
const placeholder = props.placeholder ?? <div className="flex gap-8 items-center font-medium text-gray-600"> <span className="w-32 h-32 bg-gray-50 border border-gray-100 rounded-full flex justify-center items-center"><FiPlus /></span> Tag a project </div>
|
||||
const placeholder = props.placeholder ?? <div className="flex gap-8 items-center text-gray-500"> <span className="w-32 h-32 bg-gray-50 border border-gray-100 rounded-full flex justify-center items-center"><FiPlus /></span> Tag a project </div>
|
||||
|
||||
const handleChange = (newValue: OnChangeValue<Project, false>,) => {
|
||||
props.onChange?.(newValue);
|
||||
|
||||
@@ -79,7 +79,7 @@ export default function FeedPage() {
|
||||
filterChanged={setSortByFilter}
|
||||
/>
|
||||
</div>
|
||||
<div id="content">
|
||||
<div id="content" className='pt-16 md:pt-0'>
|
||||
<PostsList
|
||||
isLoading={feedQuery.loading}
|
||||
items={feedQuery.data?.getFeed}
|
||||
@@ -88,22 +88,23 @@ export default function FeedPage() {
|
||||
/>
|
||||
</div>
|
||||
<aside id='categories' className='no-scrollbar'>
|
||||
<div className="pb-16 md:overflow-y-scroll sticky-side-element">
|
||||
<h1 className="text-h2 font-bolder mb-24">Discover</h1>
|
||||
<Button
|
||||
href={createRoute({ type: "write-story" })}
|
||||
color='primary'
|
||||
fullWidth
|
||||
>
|
||||
Write a story
|
||||
</Button>
|
||||
<div className="my-24"></div>
|
||||
<div className="my-24"></div>
|
||||
<PopularTagsFilter
|
||||
value={tagFilter}
|
||||
onChange={setTagFilter as any}
|
||||
/>
|
||||
|
||||
<div className="md:overflow-y-scroll sticky-side-element flex flex-col gap-16 md:gap-24">
|
||||
<h1 className="text-h2 font-bolder order-1">Discover</h1>
|
||||
<div className='order-3 md:order-2'>
|
||||
<Button
|
||||
href={createRoute({ type: "write-story" })}
|
||||
color='primary'
|
||||
fullWidth
|
||||
>
|
||||
Write a story
|
||||
</Button>
|
||||
</div>
|
||||
<div className='order-2 md:order-3'>
|
||||
<PopularTagsFilter
|
||||
value={tagFilter}
|
||||
onChange={setTagFilter as any}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
<aside id='side' className='no-scrollbar'>
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
.grid {
|
||||
display: grid;
|
||||
// grid-template-columns: 1fr;
|
||||
gap: 32px;
|
||||
gap: 16px;
|
||||
|
||||
& > * {
|
||||
min-width: 0;
|
||||
@@ -44,8 +44,8 @@
|
||||
|
||||
grid-template-areas:
|
||||
"title"
|
||||
"sort-by"
|
||||
"categories"
|
||||
"sort-by"
|
||||
"content";
|
||||
|
||||
:global {
|
||||
@@ -69,6 +69,7 @@
|
||||
}
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
gap: 32px;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
grid-template-rows: auto auto 1fr;
|
||||
|
||||
|
||||
@@ -15,12 +15,13 @@ interface Props {
|
||||
|
||||
export default function PostPageHeader(props: Props) {
|
||||
|
||||
|
||||
const dateToShow = () => {
|
||||
const passedTime = dayjs().diff(props.date, 'hour');
|
||||
if (passedTime === 0) return 'now';
|
||||
if (passedTime < 24) return `${dayjs().diff(props.date, 'hour')}h ago`
|
||||
return dayjs(props.date).format('MMMM DD');
|
||||
const passedTimeHrs = dayjs().diff(props.date, 'hour');
|
||||
const passedTimesDays = Math.ceil(passedTimeHrs / 24);
|
||||
if (passedTimeHrs === 0) return 'now';
|
||||
if (passedTimeHrs < 24) return `${dayjs().diff(props.date, 'hour')}h ago`
|
||||
if (passedTimesDays < 29) return `${passedTimesDays} days`
|
||||
return dayjs(props.date).format('DD MMM');
|
||||
}
|
||||
|
||||
if (!props.author) return null
|
||||
@@ -47,7 +48,7 @@ export default function PostPageHeader(props: Props) {
|
||||
</Link>
|
||||
</>}
|
||||
</span>
|
||||
<p className="text-body5 text-gray-500 font-medium">Published {dateToShow()}</p>
|
||||
<p className="text-body5 text-gray-500">Published {dateToShow()}</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -7,10 +7,13 @@ import IconButton from "src/Components/IconButton/IconButton";
|
||||
import { Menu, MenuItem } from "@szhsin/react-menu";
|
||||
import { useAppSelector } from "src/utils/hooks";
|
||||
import { useUpdateStory } from './useUpdateStory'
|
||||
import { FaPen } from "react-icons/fa";
|
||||
import DOMPurify from 'dompurify';
|
||||
import Card from "src/Components/Card/Card";
|
||||
import PostPageHeader from "../PostPageHeader/PostPageHeader";
|
||||
import { FiEdit2, FiLink } from "react-icons/fi";
|
||||
import CopyToClipboard from "react-copy-to-clipboard";
|
||||
import { createRoute } from "src/utils/routing";
|
||||
import { NotificationsService } from "src/services";
|
||||
|
||||
|
||||
interface Props {
|
||||
@@ -30,33 +33,46 @@ export default function StoryPageContent({ story }: Props) {
|
||||
<>
|
||||
<div id="content" className="bg-white md:p-32 md:border-2 border-gray-200 rounded-16 relative"> </div>
|
||||
<Card id="content" onlyMd className="relative max">
|
||||
<PostPageHeader
|
||||
className="mb-16"
|
||||
author={story.author}
|
||||
project={story.project}
|
||||
date={story.createdAt} />
|
||||
|
||||
<div className="flex justify-between items-center flex-wrap mb-16">
|
||||
<PostPageHeader
|
||||
author={story.author}
|
||||
project={story.project}
|
||||
date={story.createdAt} />
|
||||
<div className="shrink-0 text-gray-400">
|
||||
<CopyToClipboard
|
||||
text={createRoute({ type: "story", title: story.title, id: story.id })}
|
||||
onCopy={() => NotificationsService.info(" Copied share link to clipboard", { icon: "📋" })}
|
||||
>
|
||||
<IconButton>
|
||||
<FiLink />
|
||||
</IconButton>
|
||||
</CopyToClipboard>
|
||||
{curUser?.id === story.author.id && <Menu
|
||||
menuClassName='!p-8 !rounded-12'
|
||||
menuButton={<IconButton className="text-gray-400"><FiEdit2 /></IconButton>}>
|
||||
<MenuItem
|
||||
onClick={handleEdit}
|
||||
className='!p-16 font-medium flex gap-16 hover:bg-gray-100 !rounded-12'
|
||||
>
|
||||
Edit story
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
onClick={handleDelete}
|
||||
className='!p-16 font-medium flex gap-16 hover:bg-gray-100 !rounded-12'
|
||||
>
|
||||
Delete
|
||||
</MenuItem>
|
||||
</Menu>}
|
||||
</div>
|
||||
</div>
|
||||
{story.cover_image &&
|
||||
<img src={story.cover_image}
|
||||
className='w-full h-[120px] md:h-[240px] object-cover rounded-12 mb-16'
|
||||
className='w-full min-h-[120px] max-h-[320px] object-cover rounded-12 mb-16'
|
||||
// className='w-full object-cover rounded-12 md:rounded-16 mb-16'
|
||||
alt="" />}
|
||||
<div className="flex flex-col gap-24 relative">
|
||||
{curUser?.id === story.author.id && <Menu
|
||||
menuClassName='!p-8 !rounded-12'
|
||||
menuButton={<IconButton className="absolute top-0 right-0 text-gray-400"><FaPen /></IconButton>}>
|
||||
<MenuItem
|
||||
onClick={handleEdit}
|
||||
className='!p-16 font-medium flex gap-16 hover:bg-gray-100 !rounded-12'
|
||||
>
|
||||
Edit story
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
onClick={handleDelete}
|
||||
className='!p-16 font-medium flex gap-16 hover:bg-gray-100 !rounded-12'
|
||||
>
|
||||
Delete
|
||||
</MenuItem>
|
||||
</Menu>}
|
||||
|
||||
<h1 className="text-[42px] leading-[58px] font-bolder">{story.title}</h1>
|
||||
{story.tags.length > 0 && <div className="flex flex-wrap gap-8">
|
||||
{story.tags.map(tag => <Badge key={tag.id} size='sm'>
|
||||
|
||||
@@ -18,7 +18,7 @@ export default function SkillsCard({ skills, isOwner }: Props) {
|
||||
<p className="text-gray-700 text-body4">No skills added</p>
|
||||
{isOwner && <Button color='primary' className='mt-16' size='sm' href='/edit-profile/roles-skills'>Add skills</Button>}
|
||||
</>}
|
||||
<ul className=' flex flex-wrap gap-x-8 gap-y-20'>
|
||||
<ul className=' flex flex-wrap gap-8'>
|
||||
{skills.map((skill) => <li key={skill.id} className="text-body5 border border-gray-200 px-12 py-4 bg-gray-100 rounded-48 font-medium">{skill.title}</li>)}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user