Files
portfolio/web/src/components/Project/Projects/Projects.tsx
Ahmed Al-Taiar 0283c293ef
All checks were successful
Publish Docker Image / Publish Docker Image (push) Successful in 39s
An attempt to fix the PDF iframe not loading properly when the API domain is third-partyBasic printer CRUD
2025-04-06 18:08:05 -04:00

173 lines
6.0 KiB
TypeScript
Executable File

import { mdiDotsVertical } from '@mdi/js'
import Icon from '@mdi/react'
import { isAfter } from 'date-fns'
import parseHtml from 'react-html-parser'
import type {
DeleteProjectMutation,
DeleteProjectMutationVariables,
FindProjects,
} from 'types/graphql'
import { Link, routes } from '@redwoodjs/router'
import { useMutation } from '@redwoodjs/web'
import type { TypedDocumentNode } from '@redwoodjs/web'
import { toast } from '@redwoodjs/web/toast'
import { QUERY } from 'src/components/Project/ProjectsCell'
import { calculateLuminance } from 'src/lib/color'
import { timeTag, truncate } from 'src/lib/formatters'
import { batchDelete } from 'src/lib/tus'
const DELETE_PROJECT_MUTATION: TypedDocumentNode<
DeleteProjectMutation,
DeleteProjectMutationVariables
> = gql`
mutation DeleteProjectMutation($id: Int!) {
deleteProject(id: $id) {
id
}
}
`
const ProjectsList = ({ projects }: FindProjects) => {
const [deleteProject] = useMutation(DELETE_PROJECT_MUTATION, {
onCompleted: () => toast.success('Project deleted'),
onError: (error) => toast.error(error.message),
refetchQueries: [{ query: QUERY }],
awaitRefetchQueries: true,
})
const onDeleteClick = (id: DeleteProjectMutationVariables['id']) => {
if (confirm('Are you sure you want to delete project ' + id + '?')) {
batchDelete(projects.find((project) => project.id === id).images)
deleteProject({ variables: { id } })
}
}
return (
<div className="w-full overflow-hidden overflow-x-auto rounded-xl bg-base-100">
<table className="table">
<thead className="bg-base-200 font-syne">
<tr>
<th>Title</th>
<th>Description</th>
<th>Date</th>
<th>Images</th>
<th>Tags</th>
<th>Links</th>
<th className="w-0">&nbsp;</th>
</tr>
</thead>
<tbody>
{projects
.slice()
.sort((a, b) =>
isAfter(new Date(b.date), new Date(a.date)) ? 1 : -1
)
.map((project) => {
const actionButtons = (
<>
<Link
to={routes.adminProject({ id: project.id })}
title={'Show project ' + project.id + ' detail'}
className="btn btn-xs uppercase"
>
Show
</Link>
<Link
to={routes.editProject({ id: project.id })}
title={'Edit project ' + project.id}
className="btn btn-primary btn-xs uppercase"
>
Edit
</Link>
<button
type="button"
title={'Delete projectt ' + project.id}
className="btn btn-error btn-xs uppercase"
onClick={() => onDeleteClick(project.id)}
>
Delete
</button>
</>
)
return (
<tr key={project.id}>
<td>{truncate(project.title)}</td>
<td className="max-w-72">
<article className="prose text-sm line-clamp-3">
{parseHtml(project.description)}
</article>
</td>
<td className="max-w-36">{timeTag(project.date)}</td>
<td>{`${project.images.length} ${project.images.length === 1 ? 'image' : 'images'}`}</td>
<td>
<div className="flex flex-wrap gap-2">
{project.tags.map((tag, i) => (
<div
key={i}
className="badge whitespace-nowrap"
style={{
backgroundColor: tag.color,
color:
calculateLuminance(tag.color) > 0.5
? 'black'
: 'white',
}}
>
{tag.tag}
</div>
))}
</div>
</td>
<td>
<div className="flex flex-wrap gap-2">
{project.links.map((link, i) => (
<a
href={link}
target="_blank"
className="badge badge-ghost text-nowrap"
key={i}
rel="noreferrer"
>
{link}
</a>
))}
</div>
</td>
<td>
<nav className="hidden justify-end space-x-2 md:flex">
{actionButtons}
</nav>
<div className="dropdown dropdown-end flex justify-end md:hidden">
<div
tabIndex={0}
role="button"
className="btn btn-square btn-ghost btn-sm lg:hidden"
>
<Icon
path={mdiDotsVertical}
className="text-base-content-100 size-6"
/>
</div>
<div
// eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex
tabIndex={0}
className="menu dropdown-content z-10 -mt-1 mr-10 inline rounded-box bg-base-100 shadow-xl"
>
<nav className="w-46 space-x-2">{actionButtons}</nav>
</div>
</div>
</td>
</tr>
)
})}
</tbody>
</table>
</div>
)
}
export default ProjectsList