Skip to content

Commit

Permalink
places UI addition (#33)
Browse files Browse the repository at this point in the history
  • Loading branch information
andrewwippler authored Mar 27, 2023
1 parent 5ba50c3 commit a5242e1
Show file tree
Hide file tree
Showing 9 changed files with 158 additions and 53 deletions.
2 changes: 1 addition & 1 deletion api/app/Controllers/Http/PlacesController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ export default class PlacesController {
}

const place = await Place.create(posted)

await illustration.related('places').save(place)
return response.send({message: 'Created successfully', id: place.id})
}

Expand Down
1 change: 1 addition & 0 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"@types/node": "18.15.3",
"@types/react": "18.0.28",
"@types/react-dom": "18.0.11",
"date-fns": "^2.29.3",
"eslint": "8.36.0",
"eslint-config-next": "13.2.4",
"iron-session": "^6.3.1",
Expand Down
2 changes: 0 additions & 2 deletions frontend/src/components/ConfirmDialog.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
// @ts-nocheck
import { Fragment, useRef } from 'react'
import { Dialog, Transition } from '@headlessui/react'
import { ExclamationTriangleIcon } from '@heroicons/react/24/outline'
import { useAppSelector, useAppDispatch } from '@/hooks'
import { selectModal, setModal } from '@/features/modal/reducer'
import { AppProps } from 'next/app'

export default function ConfirmDialog({ handleAgree, title, deleteName }: {handleAgree: () => void, title: string | undefined, deleteName: string}) {

Expand Down
4 changes: 2 additions & 2 deletions frontend/src/components/Layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ export default function Layout({ children }: { children: React.ReactNode }) {
<Header />
<Flash />
<main className="flex min-h-full items-center justify-center py-12 px-4 sm:px-6 lg:px-8">
<div className="w-full max-w-screen-lg space-y-8">{children}</div>
<div className="w-full max-w-screen-lg">{children}</div>
</main>
<footer className="flex min-h-full items-center justify-center py-12 px-4 sm:px-6 lg:px-8">
<div className="w-full max-w-screen-lg space-y-8 text-sky-500">
<div className="w-full max-w-screen-lg text-sky-500">
Speaker Windows &copy; Copyright 2017-{moment().year()} Andrew Wippler
</div>
</footer>
Expand Down
27 changes: 27 additions & 0 deletions frontend/src/components/PlaceConfirmDialog.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { Fragment, useRef } from 'react'
import { useAppSelector, useAppDispatch } from '@/hooks'
import { selectModal, setModal } from '@/features/modal/reducer'
import ConfirmDialog from './ConfirmDialog'
import api from '@/library/api'
import { setUpdateUI } from '@/features/ui/reducer'
import { setFlashMessage } from '@/features/flash/reducer'

export default function PlaceConfirmDialog({ title, id }: {title: string | undefined, id: string | undefined}) {

const dispatch = useAppDispatch()
const placeId = id

const handlePlaceDelete = () => {
// delete illustration
api.delete(`/places/${placeId}`, '')
.then(data => {
dispatch(setModal(false))
dispatch(setUpdateUI(true))
dispatch(setFlashMessage({severity: 'danger', message: data.message}))
});
};

return (
<ConfirmDialog handleAgree={handlePlaceDelete} title={title} deleteName="Place" />
)
}
2 changes: 2 additions & 0 deletions frontend/src/library/illustrationType.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { placeType } from "./placeType";
import { tagType } from "./tagtype";

export type illustrationType = {
content: string;
tags: Array<tagType>;
places: Array<placeType>;
source: string;
author: string;
title: string;
Expand Down
3 changes: 2 additions & 1 deletion frontend/src/library/placeType.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export type placeType = {
id: string | undefined
place: string,
location: string,
used: Date,
used: string,
illustration_id: number
}
165 changes: 118 additions & 47 deletions frontend/src/pages/illustration/[id].tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,21 @@
import { useRouter } from 'next/router'
import * as _ from "lodash";
import api from '@/library/api';
import { useState, useEffect } from 'react'
import { useState, useEffect, FormEvent } from 'react'
import Link from 'next/link';
import Layout from '@/components/Layout';
import useUser from '@/library/useUser';
import { ClipboardDocumentListIcon, ArrowLeftIcon, PencilSquareIcon, TrashIcon } from '@heroicons/react/24/solid'
import { ClipboardDocumentListIcon, ArrowLeftIcon, PencilSquareIcon, TrashIcon, PlusIcon } from '@heroicons/react/24/solid'
import ConfirmDialog from '@/components/ConfirmDialog';
import { useAppSelector, useAppDispatch } from '@/hooks'
import { selectModal, setModal } from '@/features/modal/reducer'
import { setFlashMessage } from '@/features/flash/reducer'
import IllustrationForm from '@/components/IllustrationForm';
import { illustrationType } from '@/library/illustrationType';
import { selectIllustrationEdit, setIllustrationEdit, selectUpdateUI, setUpdateUI } from '@/features/ui/reducer';
import format from 'date-fns/format';
import PlaceConfirmDialog from '@/components/PlaceConfirmDialog';
import { placeType } from '@/library/placeType';

export default function IllustrationWrapper() {
const router = useRouter()
Expand All @@ -26,6 +29,7 @@ export default function IllustrationWrapper() {
})

const [illustration, setData] = useState<illustrationType>()
const [deletePlace, setdeletePlace] = useState<placeType | null>()
const [isLoading, setLoading] = useState(false)

useEffect(() => {
Expand Down Expand Up @@ -54,19 +58,49 @@ export default function IllustrationWrapper() {
return url.protocol === "http:" || url.protocol === "https:";
}

// delete illustration
const handleDelete = () => {
// delete illustration
api.delete(`/illustration/${illustration?.id}`, '')
.then(data => {
dispatch(setModal(false))
// dispatch(setFlash({message: 'something', type: 'good, bad, etc'}))
dispatch(setFlashMessage({severity: 'danger', message: `Illustration: "${illustration?.title}" was deleted.`}))
router.back()
// setData(data);
});
};

const handleDeletePlace = (place: placeType) => {
setdeletePlace(place)
dispatch(setModal(true))
}

const handleDeleteIllustration = () => {
setdeletePlace(null)
dispatch(setModal(true))
}

const handlePlaceAdd = (event: FormEvent<HTMLFormElement>) => {
event.preventDefault()
let form = grabAndReturnObject(event.currentTarget)
console.log(form)
api.post(`/places/${illustration?.id}`, form)
.then(data => {
if (data.message != 'Created successfully') {
dispatch(setFlashMessage({ severity: 'danger', message: data.message }))
return
}
dispatch(setUpdateUI(true))
dispatch(setFlashMessage({severity: 'success', message: `Added ${form.place}, ${form.location} to "${illustration?.title}"`}))
});
};

const grabAndReturnObject = (form: EventTarget & HTMLFormElement) => {
return {
place: form.Place.value.trim() || 'Someplace',
location: form.Location.value.trim() || 'Somewhere',
used: form.Used.value.trim(),
}
}

return (
<Layout>
{editIllustration ?
Expand Down Expand Up @@ -98,10 +132,10 @@ export default function IllustrationWrapper() {
</div>
<div className="columns-1">
{illustration?.content && <button type="button" data-toggle="tooltip" data-placement="bottom" title="Copy to clipboard"
className="group relative flex w-full justify-center px-4 py-2 my-2 font-semibold text-sm bg-gray-300 hover:bg-gray-500 text-white rounded-md shadow-sm"
onClick={() => { navigator.clipboard.writeText(illustration.content) }}><ClipboardDocumentListIcon className="h-4 w-4 mr-2" /> <span>Copy</span></button>}
<div className="columns-1">
{illustration?.content ? illustration.content : 'Default Title'}
className="flex w-full justify-center px-4 py-2 my-4 font-semibold text-medium bg-gray-300 hover:bg-gray-500 text-white rounded-md shadow-sm"
onClick={() => { navigator.clipboard.writeText(illustration.content) }}><ClipboardDocumentListIcon className="h-6 w-6 mr-2" /> <span>Copy Illustration Content</span></button>}
<div className="py-4">
{illustration?.content ? illustration.content : 'No Content'}
</div>

<div className="columns-1 pt-2">
Expand All @@ -112,49 +146,86 @@ export default function IllustrationWrapper() {
</button>
<button onClick={() => dispatch(setIllustrationEdit(true))} className='px-4 py-2 mr-4 mt-2 font-semibold text-sm bg-green-300 hover:bg-green-500 text-white rounded-full shadow-sm inline-flex items-center' >
<PencilSquareIcon className="h-4 w-4 mr-2" />Edit Illustration</button>
<ConfirmDialog handleAgree={handleDelete} title={illustration?.title} deleteName="Illustration" />
<button onClick={() => dispatch(setModal(true))} className='px-4 py-2 mr-4 mt-2 font-semibold text-sm bg-red-300 hover:bg-red-500 text-white rounded-full shadow-sm inline-flex items-center'>

<button onClick={() => handleDeleteIllustration()} className='px-4 py-2 mr-4 mt-2 font-semibold text-sm bg-red-300 hover:bg-red-500 text-white rounded-full shadow-sm inline-flex items-center'>
<TrashIcon className="h-4 w-4 mr-2" />Delete Illustration</button>
</div>
</div>
{/*
<div className="row">
<div className="col-sm-12" id="illustrationPlaces">
<h3>Illustration Usage:</h3>
{/* <% unless @places.empty? %>
<% @places.each do |p| %>
<% unless p.place.blank? %><%= p.place %>, <% end %><% unless p.location.blank? %><%= p.location %> - <% end %><% unless p.used.blank? %><%= p.used.strftime("%m/%d/%Y") %><% end %>
<% end %>
<% end %> */}
{/* </div>
<div className="col-sm-4">
{/* <%= form_with(model: Place, id: :places_form) do |form| %> */}
{/* <input type="hidden" value="<%= @illustration.id %>" name="place[illustration_id]">
<div className="form-group">
{/* <%= form.text_field :place, id: :place_place, class: "form-control", placeholder: "Place" %> */}
{/* </div>
</div>
<div className="col-sm-4">
<div className="form-group">
{/* <%= form.text_field :location, id: :place_location, class: "form-control", placeholder: "Location" %> */}
{/* </div>
</div>
<div className="col-sm-4">
<div className="form-group">
{/* <%= form.text_field :used, id: :place_used, class: "form-control", value: Time.now.strftime("%m/%d/%Y") %> */}
{/* </div>
</div>
<div className="col-sm-12">
<div className="form-group">
<button id="add_place" className="btn btn-success"><i className="fa fa-plus"></i> Add Place</button>
</div>
</div>
{/* <% end %>
</>
}
{!editIllustration &&
<>
<div className="text-xl font-bold pt-8 text-sky-900">
<span className='mr-4'>Illustration Use:</span>
</div>
<div className='grid grid-cols-3 text-sky-500'>
{illustration && illustration?.places.length > 0 ? illustration.places.map((p, index) => (

</div>*/}
<div key={index} className='w-full gap-2 pb-2'>
<span className='mr-2'>{p.place}, {p.location} - {p.used}</span>
<button
onClick={() => handleDeletePlace(p)}
className='rounded-md p-2 font-semibold text-sm bg-red-300 hover:bg-red-500 text-white shadow-sm inline-flex items-center'>
<TrashIcon className="h-4 w-4" /></button>
</div>
))
:
<div className='pb-2'>No places found.</div>
}
</div>
<form className='mt-4' onSubmit={handlePlaceAdd}>
<div className="grid grid-cols-6">
<div className="col-span-2">
<label htmlFor="Place" className="sr-only block text-sm font-medium leading-6 text-gray-900">
Place
</label>
<input
type="text"
name="Place"
id="Place"
placeholder="Place"
className="block w-full rounded-l-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
/>
</div>
<div className="col-span-2">
<label htmlFor="Location" className="sr-only block text-sm font-medium leading-6 text-gray-900">
Location
</label>
<input
type="text"
name="Location"
id="Location"
placeholder="Location"
className="block w-full border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
/>
</div>
<div className="col-span-1">
<label htmlFor="Used" className="sr-only block text-sm font-medium leading-6 text-gray-900">
Used
</label>
<input
type="date"
name="Used"
id="Used"
defaultValue={format(new Date(), 'yyyy-MM-dd')}
className="form-input block w-full border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
/>
</div>
<button type='submit'
className="inline-flex justify-center rounded-r-md bg-indigo-300 px-4 py-2 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-500">
<PlusIcon className='h-5 w-5 mr-2'/>
Add Place</button>
</div>
</form>
</>
}

}
{deletePlace &&
<PlaceConfirmDialog title={deletePlace.place} id={deletePlace.id} />
}
{!deletePlace &&
<ConfirmDialog handleAgree={handleDelete} title={illustration?.title} deleteName="Illustration" />
}
</Layout>
)
}
5 changes: 5 additions & 0 deletions frontend/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1092,6 +1092,11 @@ damerau-levenshtein@^1.0.8:
resolved "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz"
integrity sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==

date-fns@^2.29.3:
version "2.29.3"
resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.29.3.tgz#27402d2fc67eb442b511b70bbdf98e6411cd68a8"
integrity sha512-dDCnyH2WnnKusqvZZ6+jA1O51Ibt8ZMRNkDZdyAyK4YfbDwa/cEmuztzG5pk6hqlp9aSBPYcjOlktquahGwGeA==

debug@^3.2.7:
version "3.2.7"
resolved "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz"
Expand Down

0 comments on commit a5242e1

Please sign in to comment.