diff --git a/api/app/Controllers/Http/PlacesController.ts b/api/app/Controllers/Http/PlacesController.ts index 40c1bae..019232f 100644 --- a/api/app/Controllers/Http/PlacesController.ts +++ b/api/app/Controllers/Http/PlacesController.ts @@ -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}) } diff --git a/frontend/package.json b/frontend/package.json index 6dbcaee..8e9b5b3 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -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", diff --git a/frontend/src/components/ConfirmDialog.tsx b/frontend/src/components/ConfirmDialog.tsx index 64c4923..9615f96 100644 --- a/frontend/src/components/ConfirmDialog.tsx +++ b/frontend/src/components/ConfirmDialog.tsx @@ -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}) { diff --git a/frontend/src/components/Layout.tsx b/frontend/src/components/Layout.tsx index 3733dd4..e61f751 100644 --- a/frontend/src/components/Layout.tsx +++ b/frontend/src/components/Layout.tsx @@ -14,10 +14,10 @@ export default function Layout({ children }: { children: React.ReactNode }) {
-
{children}
+
{children}
-
+
Speaker Windows © Copyright 2017-{moment().year()} Andrew Wippler
diff --git a/frontend/src/components/PlaceConfirmDialog.tsx b/frontend/src/components/PlaceConfirmDialog.tsx new file mode 100644 index 0000000..b2fe5af --- /dev/null +++ b/frontend/src/components/PlaceConfirmDialog.tsx @@ -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 ( + + ) +} diff --git a/frontend/src/library/illustrationType.ts b/frontend/src/library/illustrationType.ts index f5d4687..16ca955 100644 --- a/frontend/src/library/illustrationType.ts +++ b/frontend/src/library/illustrationType.ts @@ -1,8 +1,10 @@ +import { placeType } from "./placeType"; import { tagType } from "./tagtype"; export type illustrationType = { content: string; tags: Array; + places: Array; source: string; author: string; title: string; diff --git a/frontend/src/library/placeType.ts b/frontend/src/library/placeType.ts index b218e01..10fd2c0 100644 --- a/frontend/src/library/placeType.ts +++ b/frontend/src/library/placeType.ts @@ -1,6 +1,7 @@ export type placeType = { + id: string | undefined place: string, location: string, - used: Date, + used: string, illustration_id: number } diff --git a/frontend/src/pages/illustration/[id].tsx b/frontend/src/pages/illustration/[id].tsx index 6582b7a..8f3fd6b 100644 --- a/frontend/src/pages/illustration/[id].tsx +++ b/frontend/src/pages/illustration/[id].tsx @@ -1,11 +1,11 @@ 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' @@ -13,6 +13,9 @@ 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() @@ -26,6 +29,7 @@ export default function IllustrationWrapper() { }) const [illustration, setData] = useState() + const [deletePlace, setdeletePlace] = useState() const [isLoading, setLoading] = useState(false) useEffect(() => { @@ -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) => { + 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 ( {editIllustration ? @@ -98,10 +132,10 @@ export default function IllustrationWrapper() {
{illustration?.content && } -
- {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) }}> Copy Illustration Content} +
+ {illustration?.content ? illustration.content : 'No Content'}
@@ -112,49 +146,86 @@ export default function IllustrationWrapper() { - -
- {/* -
-
-

Illustration Usage:

- {/* <% 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 %> */} - {/*
- -
-{/* <%= form_with(model: Place, id: :places_form) do |form| %> */} - {/* -
- {/* <%= form.text_field :place, id: :place_place, class: "form-control", placeholder: "Place" %> */} - {/*
-
-
-
- {/* <%= form.text_field :location, id: :place_location, class: "form-control", placeholder: "Location" %> */} - {/*
-
-
-
- {/* <%= form.text_field :used, id: :place_used, class: "form-control", value: Time.now.strftime("%m/%d/%Y") %> */} - {/*
-
-
-
- -
-
- {/* <% end %> + + } + {!editIllustration && + <> +
+ Illustration Use: +
+
+ {illustration && illustration?.places.length > 0 ? illustration.places.map((p, index) => ( -
*/} +
+ {p.place}, {p.location} - {p.used} + +
+ )) + : +
No places found.
+ } +
+
+
+
+ + +
+
+ + +
+
+ + +
+ +
+
- } + +} +{deletePlace && + +} +{!deletePlace && + +} ) } diff --git a/frontend/yarn.lock b/frontend/yarn.lock index d4d591e..17c4801 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -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"