Make styled part card for displaying many in a list
This commit is contained in:
@@ -12,7 +12,7 @@ import { Router, Route, Private } from '@redwoodjs/router'
|
|||||||
const Routes = () => {
|
const Routes = () => {
|
||||||
return (
|
return (
|
||||||
<Router>
|
<Router>
|
||||||
<Route path="/" page={HomePage} name="home" />
|
<Route path="/" page={PartsPage} name="home" />
|
||||||
{/* <Route path="/blah/*" page={PartsPage} name="home" /> */}
|
{/* <Route path="/blah/*" page={PartsPage} name="home" /> */}
|
||||||
<Route notfound page={NotFoundPage} />
|
<Route notfound page={NotFoundPage} />
|
||||||
|
|
||||||
@@ -35,7 +35,6 @@ const Routes = () => {
|
|||||||
<Route path="/parts/new" page={NewPartPage} name="newPart" />
|
<Route path="/parts/new" page={NewPartPage} name="newPart" />
|
||||||
<Route path="/parts/{id}/edit" page={EditPartPage} name="editPart" />
|
<Route path="/parts/{id}/edit" page={EditPartPage} name="editPart" />
|
||||||
<Route path="/parts/{id}" page={PartPage} name="part" />
|
<Route path="/parts/{id}" page={PartPage} name="part" />
|
||||||
<Route path="/parts" page={PartsPage} name="parts" />
|
|
||||||
<Route path="/comments/new" page={NewCommentPage} name="newComment" />
|
<Route path="/comments/new" page={NewCommentPage} name="newComment" />
|
||||||
<Route path="/comments/{id}/edit" page={EditCommentPage} name="editComment" />
|
<Route path="/comments/{id}/edit" page={EditCommentPage} name="editComment" />
|
||||||
<Route path="/comments/{id}" page={CommentPage} name="comment" />
|
<Route path="/comments/{id}" page={CommentPage} name="comment" />
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ export default function ImageUploader({
|
|||||||
{isEditable && <input {...getInputProps()} />}
|
{isEditable && <input {...getInputProps()} />}
|
||||||
{(cloudinaryId || !isEditable) && <div className="relative overflow-hidden w-full h-full">
|
{(cloudinaryId || !isEditable) && <div className="relative overflow-hidden w-full h-full">
|
||||||
<CloudinaryImage
|
<CloudinaryImage
|
||||||
className="object-cover w-full h-full rounded shadow overflow-hidden"
|
className="object-cover w-full h-full shadow overflow-hidden"
|
||||||
cloudName="irevdev"
|
cloudName="irevdev"
|
||||||
publicId={cloudinaryId || 'CadHub/eia1kwru54g2kf02s2xx'}
|
publicId={cloudinaryId || 'CadHub/eia1kwru54g2kf02s2xx'}
|
||||||
width={width}
|
width={width}
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ const PartProfile = ({userPart, isEditable, onSave, loading, error}) => {
|
|||||||
className="rounded-half rounded-br-lg shadow-md border-2 border-gray-200 border-solid"
|
className="rounded-half rounded-br-lg shadow-md border-2 border-gray-200 border-solid"
|
||||||
onImageUpload={() => {}}
|
onImageUpload={() => {}}
|
||||||
aspectRatio={1}
|
aspectRatio={1}
|
||||||
imageUrl={userPart.image === 'abc' ? '': userPart.image}
|
imageUrl={userPart.image}
|
||||||
width={300}
|
width={300}
|
||||||
/>
|
/>
|
||||||
<h4 className="text-indigo-800 text-xl underline text-right py-4">{userPart?.name}</h4>
|
<h4 className="text-indigo-800 text-xl underline text-right py-4">{userPart?.name}</h4>
|
||||||
|
|||||||
@@ -1,112 +1,39 @@
|
|||||||
import { useMutation, useFlash } from '@redwoodjs/web'
|
import { useMutation, useFlash } from '@redwoodjs/web'
|
||||||
import { Link, routes } from '@redwoodjs/router'
|
import { Link, routes } from '@redwoodjs/router'
|
||||||
|
|
||||||
const DELETE_PART_MUTATION = gql`
|
import ImageUploader from 'src/components/ImageUploader'
|
||||||
mutation DeletePartMutation($id: String!) {
|
|
||||||
deletePart(id: $id) {
|
|
||||||
id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
const MAX_STRING_LENGTH = 150
|
|
||||||
|
|
||||||
const truncate = (text) => {
|
|
||||||
let output = text
|
|
||||||
if (text && text.length > MAX_STRING_LENGTH) {
|
|
||||||
output = output.substring(0, MAX_STRING_LENGTH) + '...'
|
|
||||||
}
|
|
||||||
return output
|
|
||||||
}
|
|
||||||
|
|
||||||
const jsonTruncate = (obj) => {
|
|
||||||
return truncate(JSON.stringify(obj, null, 2))
|
|
||||||
}
|
|
||||||
|
|
||||||
const timeTag = (datetime) => {
|
|
||||||
return (
|
|
||||||
<time dateTime={datetime} title={datetime}>
|
|
||||||
{new Date(datetime).toUTCString()}
|
|
||||||
</time>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const checkboxInputTag = (checked) => {
|
|
||||||
return <input type="checkbox" checked={checked} disabled />
|
|
||||||
}
|
|
||||||
|
|
||||||
const PartsList = ({ parts }) => {
|
const PartsList = ({ parts }) => {
|
||||||
const { addMessage } = useFlash()
|
|
||||||
const [deletePart] = useMutation(DELETE_PART_MUTATION, {
|
|
||||||
onCompleted: () => {
|
|
||||||
addMessage('Part deleted.', { classes: 'rw-flash-success' })
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
const onDeleteClick = (id) => {
|
|
||||||
if (confirm('Are you sure you want to delete part ' + id + '?')) {
|
|
||||||
deletePart({ variables: { id }, refetchQueries: ['PARTS'] })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="rw-segment rw-table-wrapper-responsive">
|
<section className="max-w-6xl mx-auto mt-20">
|
||||||
<table className="rw-table">
|
<ul className="grid gap-8 items-center mx-4" style={{gridTemplateColumns: 'repeat(auto-fit, minmax(16rem, 1fr))'}}>
|
||||||
<thead>
|
{parts.map(({title, mainImage, user}) => (
|
||||||
<tr>
|
<li className="rounded-lg overflow-hidden shadow-md hover:shadow-lg mx-px transform hover:-translate-y-px transition-all duration-150" key={`${user?.userName}--${title}`}><button className="w-full">
|
||||||
<th>Id</th>
|
<div className="flex items-center p-2 bg-gray-200 border-gray-300 rounded-t-lg border-t border-l border-r">
|
||||||
<th>Title</th>
|
<div className="w-8 h-8 overflow-hidden rounded-full border border-indigo-300 shadow max-w-xs">
|
||||||
<th>Description</th>
|
<ImageUploader
|
||||||
<th>Code</th>
|
className=""
|
||||||
<th>Main image</th>
|
onImageUpload={() => {}}
|
||||||
<th>Created at</th>
|
aspectRatio={1}
|
||||||
<th>Updated at</th>
|
imageUrl={user?.image}
|
||||||
<th>User id</th>
|
width={50}
|
||||||
<th> </th>
|
/>
|
||||||
</tr>
|
</div>
|
||||||
</thead>
|
<span className="font-ropa-sans ml-3 text-lg text-indigo-900">{title}</span>
|
||||||
<tbody>
|
</div>
|
||||||
{parts.map((part) => (
|
<div className="w-full overflow-hidden relative">
|
||||||
<tr key={part.id}>
|
<ImageUploader
|
||||||
<td>{truncate(part.id)}</td>
|
className=""
|
||||||
<td>{truncate(part.title)}</td>
|
onImageUpload={() => {}}
|
||||||
<td>{truncate(part.description)}</td>
|
aspectRatio={1.4}
|
||||||
<td>{truncate(part.code)}</td>
|
imageUrl={mainImage}
|
||||||
<td>{truncate(part.mainImage)}</td>
|
width={700}
|
||||||
<td>{timeTag(part.createdAt)}</td>
|
/>
|
||||||
<td>{timeTag(part.updatedAt)}</td>
|
<div className="absolute inset-0" style={{background: 'linear-gradient(19.04deg, rgba(62, 44, 118, 0.46) 10.52%, rgba(60, 54, 107, 0) 40.02%)'}} />
|
||||||
<td>{truncate(part.userId)}</td>
|
</div>
|
||||||
<td>
|
</button></li>
|
||||||
<nav className="rw-table-actions">
|
))}
|
||||||
<Link
|
</ul>
|
||||||
to={routes.part({ id: part.id })}
|
</section>
|
||||||
title={'Show part ' + part.id + ' detail'}
|
|
||||||
className="rw-button rw-button-small"
|
|
||||||
>
|
|
||||||
Show
|
|
||||||
</Link>
|
|
||||||
<Link
|
|
||||||
to={routes.editPart({ id: part.id })}
|
|
||||||
title={'Edit part ' + part.id}
|
|
||||||
className="rw-button rw-button-small rw-button-blue"
|
|
||||||
>
|
|
||||||
Edit
|
|
||||||
</Link>
|
|
||||||
<a
|
|
||||||
href="#"
|
|
||||||
title={'Delete part ' + part.id}
|
|
||||||
className="rw-button rw-button-small rw-button-red"
|
|
||||||
onClick={() => onDeleteClick(part.id)}
|
|
||||||
>
|
|
||||||
Delete
|
|
||||||
</a>
|
|
||||||
</nav>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
))}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,12 +7,13 @@ export const QUERY = gql`
|
|||||||
parts {
|
parts {
|
||||||
id
|
id
|
||||||
title
|
title
|
||||||
description
|
|
||||||
code
|
|
||||||
mainImage
|
mainImage
|
||||||
createdAt
|
createdAt
|
||||||
updatedAt
|
updatedAt
|
||||||
userId
|
user {
|
||||||
|
image
|
||||||
|
userName
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ const UserProfile = ({user, isEditable, loading, onSave, error}) => {
|
|||||||
const editableTextFields = {userName, name}
|
const editableTextFields = {userName, name}
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="max-w-2xl mx-auto mt-20 ">
|
<section className="max-w-2xl mx-auto mt-20 ">
|
||||||
<div className="flex" >
|
<div className="flex" >
|
||||||
<div className="w-40 flex-shrink-0">
|
<div className="w-40 flex-shrink-0">
|
||||||
<ImageUploader
|
<ImageUploader
|
||||||
@@ -36,7 +36,7 @@ const UserProfile = ({user, isEditable, loading, onSave, error}) => {
|
|||||||
})}
|
})}
|
||||||
aspectRatio={1}
|
aspectRatio={1}
|
||||||
isEditable={isEditable}
|
isEditable={isEditable}
|
||||||
imageUrl={user.image === 'abc' ? '': user.image}
|
imageUrl={user.image}
|
||||||
width={300}
|
width={300}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -66,7 +66,7 @@ const UserProfile = ({user, isEditable, loading, onSave, error}) => {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</section>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ const MainLayout = ({ children }) => {
|
|||||||
className="rounded-full object-cover"
|
className="rounded-full object-cover"
|
||||||
onImageUpload={() => {}}
|
onImageUpload={() => {}}
|
||||||
aspectRatio={1}
|
aspectRatio={1}
|
||||||
imageUrl={data?.user?.image === 'abc' ? '': data?.user?.image}
|
imageUrl={data?.user?.image}
|
||||||
width={80}
|
width={80}
|
||||||
/>}
|
/>}
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
Reference in New Issue
Block a user