Style part profile editable state

This commit is contained in:
Kurt Hutten
2020-11-07 19:45:37 +11:00
parent 1bdc836b66
commit 0784cdbde2
15 changed files with 225 additions and 108 deletions

View File

@@ -16,15 +16,15 @@ const Routes = () => {
{/* <Route path="/blah/*" page={PartsPage} name="home" /> */}
<Route notfound page={NotFoundPage} />
{/* Ownership enforced routes */}
<Route path="/u/{userName}/edit" page={EditUser2Page} name="editUser2" />
<Route path="/u/{userName}/{partTitle}/edit" page={EditPart2Page} name="editPart2" />
{/* End ownership enforced routes */}
<Route path="/u/{userName}" page={User2Page} name="user2" />
<Route path="/u/{userName}/{partTitle}" page={Part2Page} name="part2" />
{/* <Route path="/u/{userName}/{partTitle}/ide" page={Part2Page} name="part2" /> */}
{/* Ownership enforced routes */}
<Route path="/u/{userName}/edit" page={EditUser2Page} name="editUser2" />
{/* <Route path="/u/{userName}/{partTitle}/edit" page={Part2Page} name="part2" /> */}
{/* End ownership enforced routes */}
{/* GENERATED ROUTES BELOW, probably going to clean these up and delete most of them, but the CRUD functionality is useful for now */}
{/* All private by default for safety and because the routes that are left after clean up will probably be admin pages */}
<Private unauthenticated="home" role="admin">

View File

@@ -1,16 +1,21 @@
import { getActiveClasses } from "get-active-classes"
const Breadcrumb = ({ userName, partTitle, className }) => {
import InputText from 'src/components/InputText'
const Breadcrumb = ({ userName, partTitle, onPartTitleChange, className }) => {
return (
<h3 className={getActiveClasses("text-2xl font-roboto", className)}>
<div className="w-1 inline-block text-indigo-800 bg-indigo-800 mr-2">.</div>
<span className="text-gray-500">
<span className={getActiveClasses({"text-gray-500": !onPartTitleChange, 'text-gray-400': onPartTitleChange})}>
{userName}
</span>
<div className="w-1 inline-block bg-gray-400 text-gray-400 mx-3 transform -skew-x-20" >.</div>
<span className="text-indigo-800">
{partTitle}
</span>
<InputText
value={partTitle}
onChange={onPartTitleChange}
isEditable={onPartTitleChange}
className={getActiveClasses("text-indigo-800 text-2xl", {'-ml-2': !onPartTitleChange})}
/>
</h3>
)
}

View File

@@ -1,5 +1,6 @@
import { useMutation, useFlash } from '@redwoodjs/web'
import { navigate, routes } from '@redwoodjs/router'
import UserProfile from 'src/components/UserProfile'
export const QUERY = gql`

View File

@@ -0,0 +1,21 @@
import { getActiveClasses } from 'get-active-classes'
const InputText = ({value, isEditable, onChange ,className}) => {
return (
<>
<div className={getActiveClasses('relative inline-block', {'hidden': !isEditable}, className)}>
<div className="absolute inset-0 mb-2 rounded bg-gray-200 shadow-inner bg-gray-100" />
<input
className="pl-2 pt-1 text-indigo-800 font-medium mb-px pb-px bg-transparent relative"
onChange={onChange}
value={value}
readOnly={!onChange}
type="text"
/>
</div>
<span className={getActiveClasses('pl-2 text-indigo-800 font-medium mb-px pb-px',{'hidden': isEditable}, className)}>{value}</span>
</>
)
}
export default InputText

View File

@@ -0,0 +1,7 @@
import InputText from './InputText'
export const generated = () => {
return <InputText />
}
export default { title: 'Components/InputText' }

View File

@@ -0,0 +1,11 @@
import { render } from '@redwoodjs/testing'
import InputText from './InputText'
describe('InputText', () => {
it('renders successfully', () => {
expect(() => {
render(<InputText />)
}).not.toThrow()
})
})

View File

@@ -1,10 +1,7 @@
import Editor from "rich-markdown-editor";
import { useMutation, useFlash } from '@redwoodjs/web'
import { navigate, routes } from '@redwoodjs/router'
// import Part from 'src/components/Part'
import ImageUploader from 'src/components/ImageUploader'
import Breadcrumb from 'src/components/Breadcrumb'
import EmojiReaction from 'src/components/EmojiReaction'
import Button from 'src/components/Button'
import PartProfile from 'src/components/PartProfile'
export const QUERY = gql`
query FIND_PART_BY_USERNAME_TITLE($userName: String!, $partTitle: String!) {
@@ -34,77 +31,6 @@ export const Empty = () => <div>Empty</div>
export const Failure = ({ error }) => <div>Error: {error.message}</div>
export const Success = ({ userPart, variables }) => {
return (
<>
<div className="grid mt-20 gap-8" style={{gridTemplateColumns: 'auto 12rem minmax(12rem, 42rem) 10rem auto'}}>
{/* Side column */}
<aside className="col-start-2">
<ImageUploader
className="rounded-half rounded-br-lg shadow-md border-2 border-gray-200 border-solid"
onImageUpload={() => {}}
aspectRatio={1}
imageUrl={userPart.image === 'abc' ? '': userPart.image}
/>
<h4 className="text-indigo-800 text-xl underline text-right py-4">{userPart?.name}</h4>
<div className="h-px bg-indigo-200 mb-4" />
{/* TODO hook up to emoji data properly */}
<EmojiReaction
// emotes={[{emoji: '❤️',count: 3},{emoji: '😁',count: 2},{emoji: '😜',count: 2},{emoji: '🤩',count: 2},{emoji: '🤣',count: 2},{emoji: '🙌',count: 2},{emoji: '🚀',count: 2}]}
emotes={[{emoji: '❤️',count: 3},{emoji: '😁',count: 2}]}
// emotes={[]}
onEmote={() => {}}
/>
<Button
className="mt-6 ml-auto hover:shadow-lg bg-gradient-to-r from-transparent to-indigo-100"
shouldAnimateHover
iconName="plus"
onClick={() => {}}
>
Comments 11
</Button>
<Button
className="mt-4 ml-auto shadow-md hover:shadow-lg bg-indigo-200"
shouldAnimateHover
iconName="plus"
onClick={() => {}}
>
Open IDE
</Button>
<Button
className="mt-4 ml-auto shadow-md hover:shadow-lg bg-indigo-200"
shouldAnimateHover
iconName="pencil"
onClick={() => {}}
>
Edit Part
</Button>
</aside>
{/* main project center column */}
<section className="col-start-3">
<Breadcrumb className="mb-8" userName={userPart.userName} partTitle={userPart?.Part?.title}/>
{ userPart?.Part?.mainImage && <ImageUploader
className="rounded-lg shadow-md border-2 border-gray-200 border-solid"
onImageUpload={() => {}}
aspectRatio={16/9}
imageUrl={userPart?.Part?.mainImage}
/>}
<div name="description" className="markdown-overrides rounded-lg shadow-md bg-white p-12 my-8 min-h-md">
<Editor
defaultValue={userPart?.Part?.description || ''}
readOnly
// readOnly={!isEditable}
// onChange={(bioFn) => setInput({
// ...input,
// bio: bioFn(),
// })}
/>
</div>
</section>
</div>
</>
)
export const Success = ({ userPart, variables: {isEditable} }) => {
return <PartProfile userPart={userPart} isEditable={isEditable} />
}

View File

@@ -0,0 +1,92 @@
import {useState} from 'react'
import Editor from "rich-markdown-editor";
import ImageUploader from 'src/components/ImageUploader'
import Breadcrumb from 'src/components/Breadcrumb'
import EmojiReaction from 'src/components/EmojiReaction'
import Button from 'src/components/Button'
const PartProfile = ({userPart, isEditable}) => {
const part = userPart?.Part
const [input, setInput] = useState({
title: part.title,
mainImage: part.mainImage,
description: part.description,
})
const hi = () => {}
return (
<>
<div className="grid mt-20 gap-8" style={{gridTemplateColumns: 'auto 12rem minmax(12rem, 42rem) 10rem auto'}}>
{/* Side column */}
<aside className="col-start-2 relative">
<ImageUploader
className="rounded-half rounded-br-lg shadow-md border-2 border-gray-200 border-solid"
onImageUpload={() => {}}
aspectRatio={1}
imageUrl={userPart.image === 'abc' ? '': userPart.image}
/>
<h4 className="text-indigo-800 text-xl underline text-right py-4">{userPart?.name}</h4>
<div className="h-px bg-indigo-200 mb-4" />
{/* TODO hook up to emoji data properly */}
<EmojiReaction
// emotes={[{emoji: '❤️',count: 3},{emoji: '😁',count: 2},{emoji: '😜',count: 2},{emoji: '🤩',count: 2},{emoji: '🤣',count: 2},{emoji: '🙌',count: 2},{emoji: '🚀',count: 2}]}
emotes={[{emoji: '❤️',count: 3},{emoji: '😁',count: 2}]}
// emotes={[]}
onEmote={() => {}}
/>
<Button
className="mt-6 ml-auto hover:shadow-lg bg-gradient-to-r from-transparent to-indigo-100"
shouldAnimateHover
iconName="chevron-down"
onClick={() => {}}
>
Comments 11
</Button>
<Button
className="mt-4 ml-auto shadow-md hover:shadow-lg bg-indigo-200"
shouldAnimateHover
iconName="terminal"
onClick={() => {}}
>
Open IDE
</Button>
{true && <Button
className="mt-4 ml-auto shadow-md hover:shadow-lg bg-indigo-200 relative z-20"
shouldAnimateHover
iconName={isEditable ? 'save' : 'pencil'}
onClick={() => {}}
>
{isEditable ? 'Save Details' : 'Edit Details'}
</Button>}
{isEditable && <div className="absolute inset-0 bg-gray-300 opacity-75 z-10 transform scale-x-110 -ml-1 -mt-2" />}
</aside>
{/* main project center column */}
<section className="col-start-3">
<Breadcrumb className="inline" onPartTitleChange={isEditable && hi} userName={userPart.userName} partTitle={input?.title}/>
{ input?.mainImage && <ImageUploader
className="rounded-lg shadow-md border-2 border-gray-200 border-solid mt-8"
onImageUpload={() => {}}
aspectRatio={16/9}
isEditable={isEditable}
imageUrl={input?.mainImage}
/>}
<div name="description" className="markdown-overrides rounded-lg shadow-md bg-white p-12 my-8 min-h-md">
<Editor
defaultValue={part?.description || ''}
readOnly={!isEditable}
// onChange={(bioFn) => setInput({
// ...input,
// bio: bioFn(),
// })}
/>
</div>
</section>
</div>
</>
)
}
export default PartProfile

View File

@@ -0,0 +1,7 @@
import PartProfile from './PartProfile'
export const generated = () => {
return <PartProfile />
}
export default { title: 'Components/PartProfile' }

View File

@@ -0,0 +1,11 @@
import { render } from '@redwoodjs/testing'
import PartProfile from './PartProfile'
describe('PartProfile', () => {
it('renders successfully', () => {
expect(() => {
render(<PartProfile />)
}).not.toThrow()
})
})

View File

@@ -1,5 +1,6 @@
import {Fragment, useState} from 'react'
import { getActiveClasses } from "get-active-classes"
import {Fragment} from 'react'
import InputText from 'src/components/InputText'
const ProfileTextInput = ({fields, isEditable, onChange= () => {}}) => {
return (
@@ -8,16 +9,12 @@ const ProfileTextInput = ({fields, isEditable, onChange= () => {}}) => {
{Object.entries(fields).map(([property, value]) => (<Fragment key={property}>
<span className="capitalize text-gray-500 text-sm align-middle my-3">{property}:</span>
<div className={getActiveClasses('relative ml-2', {'hidden': !isEditable})}>
<div className="absolute inset-0 mb-2 rounded bg-gray-200 shadow-inner bg-gray-100" />
<input
className="pl-2 pt-1 text-indigo-800 font-medium text-xl mb-px pb-px bg-transparent relative"
onChange={({target}) => onChange({...fields, [property]: target.value})}
value={value}
type="text"
/>
</div>
<span className={getActiveClasses('pl-2 text-indigo-800 font-medium text-xl mb-px pb-px',{'hidden': isEditable})}>{value}</span>
<InputText
className="text-xl"
value={value}
onChange={({target}) => onChange({...fields, [property]: target.value})}
isEditable={isEditable}
/>
</Fragment>))}
</div>

View File

@@ -1,18 +1,27 @@
const Svg = ({name, className: className2, strokeWidth = 2}) => {
const svgs = {
"plus-circle": <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={strokeWidth} d="M12 9v3m0 0v3m0-3h3m-3 0H9m12 0a9 9 0 11-18 0 9 9 0 0118 0z" />
"chevron-down": <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={strokeWidth} d="M19 9l-7 7-7-7" />
</svg>,
"plus":<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={strokeWidth} d="M12 6v6m0 0v6m0-6h6m-6 0H6" />
"dots-vertical": <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={strokeWidth} d="M12 5v.01M12 12v.01M12 19v.01M12 6a1 1 0 110-2 1 1 0 010 2zm0 7a1 1 0 110-2 1 1 0 010 2zm0 7a1 1 0 110-2 1 1 0 010 2z" />
</svg>,
"pencil": <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={strokeWidth} d="M15.232 5.232l3.536 3.536m-2.036-5.036a2.5 2.5 0 113.536 3.536L6.5 21.036H3v-3.572L16.732 3.732z" />
</svg>,
"dots-vertical": <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={strokeWidth} d="M12 5v.01M12 12v.01M12 19v.01M12 6a1 1 0 110-2 1 1 0 010 2zm0 7a1 1 0 110-2 1 1 0 010 2zm0 7a1 1 0 110-2 1 1 0 010 2z" />
</svg>
"plus":<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={strokeWidth} d="M12 6v6m0 0v6m0-6h6m-6 0H6" />
</svg>,
"plus-circle": <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={strokeWidth} d="M12 9v3m0 0v3m0-3h3m-3 0H9m12 0a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>,
"save": <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M8 7H5a2 2 0 00-2 2v9a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2h-3m-1 4l-3 3m0 0l-3-3m3 3V4" />
</svg>,
"terminal": <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={strokeWidth} d="M8 9l3 3-3 3m5 0h3M5 20h14a2 2 0 002-2V6a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" />
</svg>,
}
return <div className={className2 || "h-10 w-10"}>

View File

@@ -0,0 +1,12 @@
import MainLayout from 'src/layouts/MainLayout'
import Part2Cell from 'src/components/Part2Cell'
const EditPart2Page = ({userName, partTitle}) => {
return (
<MainLayout>
<Part2Cell userName={userName} partTitle={partTitle} isEditable/>
</MainLayout>
)
}
export default EditPart2Page

View File

@@ -0,0 +1,7 @@
import EditPart2Page from './EditPart2Page'
export const generated = () => {
return <EditPart2Page />
}
export default { title: 'Pages/EditPart2Page' }

View File

@@ -0,0 +1,11 @@
import { render } from '@redwoodjs/testing'
import EditPart2Page from './EditPart2Page'
describe('EditPart2Page', () => {
it('renders successfully', () => {
expect(() => {
render(<EditPart2Page />)
}).not.toThrow()
})
})