From 6d84038f50e0ee2c4541b0b62b1f2dba02f2389d Mon Sep 17 00:00:00 2001 From: Sanna Hautala Date: Sat, 22 Jun 2024 19:52:07 +0300 Subject: [PATCH] Improvements (#21) * feat: disable message sending if sender is deceased * fix: remove extra arrow from sizePerPage change button * feat: add edit character gm_notes * chore: remove placeholder for add gm notes during the game * feat: add edit artifact gm_notes --------- Co-authored-by: Nico Hagelberg --- package-lock.json | 14 - src/api/artifact.js | 18 + src/api/character.js | 17 + src/components/Artifact.css | 19 + src/components/Artifact.js | 197 +++++---- src/components/Character.css | 23 ++ src/components/Character.js | 433 ++++++++++---------- src/components/Event.js | 25 -- src/components/Message.css | 4 + src/components/Message.js | 13 +- src/components/Plot.js | 25 -- src/components/modals/EditArtifactModal.js | 96 +++++ src/components/modals/EditCharacterModal.js | 96 +++++ 13 files changed, 590 insertions(+), 390 deletions(-) create mode 100644 src/api/artifact.js create mode 100644 src/api/character.js create mode 100644 src/components/modals/EditArtifactModal.js create mode 100644 src/components/modals/EditCharacterModal.js diff --git a/package-lock.json b/package-lock.json index a026093..b930231 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15619,20 +15619,6 @@ "is-typedarray": "^1.0.0" } }, - "node_modules/typescript": { - "version": "4.9.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", - "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", - "license": "Apache-2.0", - "peer": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=4.2.0" - } - }, "node_modules/unbox-primitive": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz", diff --git a/src/api/artifact.js b/src/api/artifact.js new file mode 100644 index 0000000..e89e473 --- /dev/null +++ b/src/api/artifact.js @@ -0,0 +1,18 @@ +import { apiUrl } from "../api"; + +export async function updateArtifactGmNotes(artifactId, gmNote) { + const requestBody = { + id: artifactId, + gm_notes: gmNote?.trim() ?? null, + }; + + const response = await fetch(apiUrl("/science/artifact"), { + method: "PUT", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(requestBody), + }); + + return response; +} diff --git a/src/api/character.js b/src/api/character.js new file mode 100644 index 0000000..8981ec3 --- /dev/null +++ b/src/api/character.js @@ -0,0 +1,17 @@ +import { apiUrl } from "../api"; + +export async function updateCharacterGmNotes(personId, gmNotes) { + const requestBody = { + gm_notes: gmNotes?.trim() ?? null, + }; + + const response = await fetch(apiUrl("/person/" + personId), { + method: "PUT", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(requestBody), + }); + + return response; +} diff --git a/src/components/Artifact.css b/src/components/Artifact.css index 3f081f8..8fa0a6d 100644 --- a/src/components/Artifact.css +++ b/src/components/Artifact.css @@ -35,3 +35,22 @@ h1.artifact { .artifact li::marker { vertical-align: top; } + +.artifact .edit-btn { + border-color: var(--gray-border-color); + padding: 4px 6px 4px 6px; + margin-left: 15px; +} + +.artifact .float-char-btn > svg { + color: var(--artifact-color); +} + +.artifact .float-char-btn:hover { + background-color:#dcdbdb; + border-color: var(--gray-border-color); +} + +.artifact .float-char-btn:focus { + box-shadow: 0 0 0 .2rem var(--gray-border-color); +} \ No newline at end of file diff --git a/src/components/Artifact.js b/src/components/Artifact.js index cd93ee1..df4be6a 100644 --- a/src/components/Artifact.js +++ b/src/components/Artifact.js @@ -5,10 +5,14 @@ import { Link } from "react-router-dom"; import { apiGetRequest } from "../api"; import TableLoading from "./TableLoading"; import useSWR from "swr"; +import { Button } from "react-bootstrap"; +import { BiPencil } from "react-icons/bi"; +import EditArtifactModal from "./modals/EditArtifactModal"; import './Artifact.css'; export default function Artifact(props) { + const [showArtifactEdit, setShowArtifactEdit] = React.useState(false); const params = useParams(); const swrArtifact = useSWR( @@ -21,7 +25,7 @@ export default function Artifact(props) { apiGetRequest, ); - const swrPerson = useSWR(() => + const swrPerson = useSWR(() => "/person/search/" + swrArtifact.data.discovered_by, apiGetRequest, ); @@ -38,114 +42,101 @@ export default function Artifact(props) { const renderArtifact = () => { if (!artifact || !artifactDetails) return null; - + const artifact_entries = artifact.entries.map((e) => e.entry.split('\n')).flat(); const artifact_notes = artifact.gm_notes ? artifact.gm_notes.split('\n') : []; const test_result_none = "No significant findings were discovered." return ( -
- - - Basic Info - - - Name: {artifact.name} - ID: {artifact.id} - - - Origin: {artifact.type ?? '-'} - Catalog ID: {artifact.catalog_id} - - - Visible on DataHub: {artifact.is_visible ? "Yes" : "No"} - - - Discovery - - - Discovered At: {artifact.discovered_at ?? '-'} - - - Discovered By: {artifact.discovered_by ? props.changeTab('Characters')} to={`/characters/${discoveredById}`}>{artifact.discovered_by} : '-'} - - - Discovered From: {artifact.discovered_from ?? '-'} - - - Artifact Description - - - {artifact.text ? artifact.text.split('![]')[0].trim() : 'No description available'} - - - Plots - {artifactDetails.plots.length < 1 ?
  • No linked plots
:
    {artifactDetails.plots.map(p =>
  • - props.changeTab('Plots')} to={`/plots/${p.id}`}>{p.name}
  • )} -
} - - Events - {artifactDetails.events.length < 1 ?
  • No linked events
:
    {artifactDetails.events.map(e =>
  • - props.changeTab('Events')} to={`/events/${e.id}`}>{e.name}
  • )} -
} - -
- - Test Results - - -

Age: {artifact.test_age ? artifact.test_age : test_result_none}

-
- -

History: {artifact.test_history ? artifact.test_history : test_result_none}

-
- -

Material: {artifact.test_material ? artifact.test_material : test_result_none}

-
- -

Microscope: {artifact.test_microscope ? artifact.test_microscope : test_result_none}

-
- -

X-ray fluorecense: {artifact.test_xrf ? artifact.test_xrf : test_result_none}

-
- - GM Notes - - {artifact_notes.length < 1 ?
  • No notes
: -
    {artifact_notes.map(n =>
  • {n}
  • )}
}
- - GM Notes During the Runs [ADD NOTE BUTTON] [HIDE PREVIOUS RUNS CHECKBOX] - -
  • - Timestamp: Note 6 -
  • -
  • - Timestamp: Note 5 -
  • -
  • - Timestamp: Note 4 -
  • -
  • - Timestamp: Note 3 -
  • -
  • - Timestamp: Note 2 -
  • -
  • - Timestamp: Note 1 -
  • -
- -

Save the notes between games!

-
- - Artifact Entries - - {artifact_entries.length < 1 ?
  • No entries
:
    - {artifact_entries.map(e => e ?
  • {e}
  • : null)} -
} -
+
+
+ + + Basic Info + + + Name: {artifact.name} + ID: {artifact.id} + + + Origin: {artifact.type ?? '-'} + Catalog ID: {artifact.catalog_id} + + + Visible on DataHub: {artifact.is_visible ? "Yes" : "No"} + + + Discovery + + + Discovered At: {artifact.discovered_at ?? '-'} + + + Discovered By: {artifact.discovered_by ? props.changeTab('Characters')} to={`/characters/${discoveredById}`}>{artifact.discovered_by} : '-'} + + + Discovered From: {artifact.discovered_from ?? '-'} + + + Artifact Description + + + {artifact.text ? artifact.text.split('![]')[0].trim() : 'No description available'} + + + Plots + {artifactDetails.plots.length < 1 ?
  • No linked plots
:
    {artifactDetails.plots.map(p =>
  • + props.changeTab('Plots')} to={`/plots/${p.id}`}>{p.name}
  • )} +
} + + Events + {artifactDetails.events.length < 1 ?
  • No linked events
:
    {artifactDetails.events.map(e =>
  • + props.changeTab('Events')} to={`/events/${e.id}`}>{e.name}
  • )} +
} + +
+ + Test Results + + +

Age: {artifact.test_age ? artifact.test_age : test_result_none}

+
+ +

History: {artifact.test_history ? artifact.test_history : test_result_none}

+
+ +

Material: {artifact.test_material ? artifact.test_material : test_result_none}

+
+ +

Microscope: {artifact.test_microscope ? artifact.test_microscope : test_result_none}

+
+ +

X-ray fluorecense: {artifact.test_xrf ? artifact.test_xrf : test_result_none}

+
+ + + GM Notes + + + + + {artifact_notes.length < 1 ?
  • No notes
: +
    {artifact_notes.map(n =>
  • {n}
  • )}
}
+ + Artifact Entries + + {artifact_entries.length < 1 ?
  • No entries
:
    + {artifact_entries.map(e => e ?
  • {e}
  • : null)} +
} +
+
+ setShowArtifactEdit(false)} + onEditDone={swrArtifact.mutate} + />
) } diff --git a/src/components/Character.css b/src/components/Character.css index 17a346e..e96d86d 100644 --- a/src/components/Character.css +++ b/src/components/Character.css @@ -33,4 +33,27 @@ h1.character { white-space: pre-line; max-width: 150ch; display: inline-block; +} + +span.caret { + display: none; +} + +.character .edit-btn { + border-color: var(--gray-border-color); + padding: 4px 6px 4px 6px; + margin-left: 15px; +} + +.character .float-char-btn > svg { + color: var(--character-color); +} + +.character .float-char-btn:hover { + background-color:#dcdbdb; + border-color: var(--gray-border-color); +} + +.character .float-char-btn:focus { + box-shadow: 0 0 0 .2rem var(--gray-border-color); } \ No newline at end of file diff --git a/src/components/Character.js b/src/components/Character.js index 7ef06d1..22aa2be 100644 --- a/src/components/Character.js +++ b/src/components/Character.js @@ -6,6 +6,9 @@ import FloatingButtons from "./FloatingButtons"; import { apiGetRequest } from "../api"; import TableLoading from "./TableLoading"; import useSWR from "swr"; +import { Button } from "react-bootstrap"; +import { BiPencil } from "react-icons/bi"; +import EditCharacterModal from "./modals/EditCharacterModal"; import './Character.css'; @@ -21,11 +24,12 @@ const getIsCharacterText = (character) => { } export default function Character(props) { + const [showCharacterEdit, setShowCharacterEdit] = React.useState(false); const params = useParams(); - const swrCharacter = useSWR( "/person/" + params.id, apiGetRequest); - const swrCharacterStory = useSWR( "/story/person/" + params.id, apiGetRequest); - const swrFleet = useSWR( "/fleet?show_hidden=true", apiGetRequest); + const swrCharacter = useSWR("/person/" + params.id, apiGetRequest); + const swrCharacterStory = useSWR("/story/person/" + params.id, apiGetRequest); + const swrFleet = useSWR("/fleet?show_hidden=true", apiGetRequest); const isLoading = swrCharacter.isLoading || swrCharacterStory.isLoading || swrFleet.isLoading; const error = swrCharacter.error || swrCharacterStory.error || swrFleet.error; @@ -59,224 +63,211 @@ export default function Character(props) { const gm_notes_list = character.gm_notes ? character.gm_notes.split('\n') : []; return ( -
- - - - {character.link_to_character ? Link to Character document : ""} - - - - Basic Info - - - Name: {character.full_name} - Age: {542 - character.birth_year} - Card ID: {character.card_id} - - - Title: {character.title ? character.title : "-"} - Birth Year: {character.birth_year} - Bio ID: {character.bio_id} - - - Occupation: {character.occupation} - File created: {character.created_year} - Citizen ID: {character.citizen_id} - - - Wartime role: {character.role ? character.role : "-"} - Home planet: {character.home_planet} - Database ID: {character.id} - - - Military Rank: {character.military_rank ? character.military_rank : "-"} - - - Shift: {character.shift ? character.shift : "-"} - - - Social Details - - - Social Class: {character.social_class} - Religion: {character.religion} - - - Dynasty: {character.dynasty} - Political Party: {character.political_party} - - - Current Status - - - Status: {character.status} - Ship: {character.ship ? props.changeTab('Fleet')} to={`/fleet/${character.ship.id}`}>{character.ship.name} : "None"} - - - GM Information - - - Character group: {character.character_group ? character.character_group : "-"} - Elder gene: {character.medical_elder_gene ? "Yes" : "No"} - - - Additional role: {character.role_additional ? character.role_additional : "-"} - - - Special group: {character.special_group ? character.special_group : "-"} - - - Plots - {characterStory?.plots.length < 1 ?
  • No linked plots
:
    {characterStory?.plots.map(p =>
  • - props.changeTab('Plots')} to={`/plots/${p.id}`}>{p.name}
  • )} -
- }
- - Events - {characterStory?.events.length < 1 ?
  • No linked Events
:
    {characterStory?.events.map(e =>
  • - props.changeTab('Events')} to={`/events/${e.id}`}>{e.name}
  • )} -
- }
- -
- - Messages - {characterStory.messages.length < 1 ?
  • No messages
:
    {characterStory.messages.map(m =>
  • - props.changeTab('Messages')} to={`/messages/${m.id}`}>{m.name} - Sent: {m.sent}
  • )} -
- }
- -
- - Summary - - {character_summary.length < 1 ?

No summary

:
    {character_summary.map(n =>
  • {n}
  • )} -
}
- - GM Notes - - - - {gm_notes_list.length <1 ?
  • No notes
: -
    {gm_notes_list.map(n =>
  • {n}
  • )}
}
- -
- - GM Notes During the Runs [ADD NOTE BUTTON] [HIDE PREVIOUS RUNS CHECKBOX] - -
  • - Timestamp: Note 6 -
  • -
  • - Timestamp: Note 5 -
  • -
  • - Timestamp: Note 4 -
  • -
  • - Timestamp: Note 3 -
  • -
  • - Timestamp: Note 2 -
  • -
  • - Timestamp: Note 1 -
  • -
- - Save the notes between games! - - - Family - - - {family_list.length ?
    {family_list}
:
  • None
} -
- - Other known relations - - - {relations_list.length ?
    {relations_list}
:
  • None
}
-
- - Personal - - - {personal_history_list ?
    {personal_history_list}
:
  • None
} -
- - Classified personal data - - - {character.personal_secret_info ? character.personal_secret_info : "No classified personal data"} - - - Military - - - - Military Academies: - {military_academies_list ?
    {military_academies_list}
: "None"} - - Military Rank: {character.military_rank ? character.military_rank : "-"} -
- - - Military Remarks:{!character.military_remarks && " None"} - {character.military_remarks &&
    {character.military_remarks.split('\n\n').filter(Boolean).map(r =>
  • {r}
  • )}
} - -
- - Military Service History - - - {military_history_list?.length ?
    {military_history_list}
:
  • None
} -
- - Medical - - - Fitness Level: {character.medical_fitness_level ? character.medical_fitness_level : "-"} - Blood Type: {character.medical_blood_type ? character.medical_blood_type : "-"} - Last Fitness Check: {character.medical_last_fitness_check ? character.medical_last_fitness_check : "-"} - - - - Active Conditions{!character.medical_active_conditions && : None} - {character.medical_active_conditions &&
    {character.medical_active_conditions.split('\n\n').filter(Boolean).map(c =>
  • {c}
  • )}
} - - - Current Medication{!character.medical_current_medication && : None} - {character.medical_current_medication &&
    {character.medical_current_medication.split('\n\n').filter(Boolean).map(m =>
  • {m}
  • )}
} - - - Allergies{!character.medical_allergies && : None} - {character.medical_allergies &&
    {character.medical_allergies.split(',').filter(Boolean).map(c =>
  • {c}
  • )}
} - -
- - Medical History{medical_history.length === 0 && : None} - - - {medical_history_list ?
    {medical_history_list}
: "None"} -
- - Groups/Roles - - {character.groups.length === 0 &&
  • None
} -
    {character.groups.map(group =>
  • {group}
  • )}
- - Metadata - - - Is Character: {getIsCharacterText(character)} - Is Visible: {character.is_visible ? "Yes" : "No"} - - -   - -
+
+
+ + + + {character.link_to_character ? Link to Character document : ""} + + + + Basic Info + + + Name: {character.full_name} + Age: {542 - character.birth_year} + Card ID: {character.card_id} + + + Title: {character.title ? character.title : "-"} + Birth Year: {character.birth_year} + Bio ID: {character.bio_id} + + + Occupation: {character.occupation} + File created: {character.created_year} + Citizen ID: {character.citizen_id} + + + Wartime role: {character.role ? character.role : "-"} + Home planet: {character.home_planet} + Database ID: {character.id} + + + Military Rank: {character.military_rank ? character.military_rank : "-"} + + + Shift: {character.shift ? character.shift : "-"} + + + Social Details + + + Social Class: {character.social_class} + Religion: {character.religion} + + + Dynasty: {character.dynasty} + Political Party: {character.political_party} + + + Current Status + + + Status: {character.status} + Ship: {character.ship ? props.changeTab('Fleet')} to={`/fleet/${character.ship.id}`}>{character.ship.name} : "None"} + + + GM Information + + + Character group: {character.character_group ? character.character_group : "-"} + Elder gene: {character.medical_elder_gene ? "Yes" : "No"} + + + Additional role: {character.role_additional ? character.role_additional : "-"} + + + Special group: {character.special_group ? character.special_group : "-"} + + + Plots + {characterStory?.plots.length < 1 ?
  • No linked plots
:
    {characterStory?.plots.map(p =>
  • + props.changeTab('Plots')} to={`/plots/${p.id}`}>{p.name}
  • )} +
+ }
+ + Events + {characterStory?.events.length < 1 ?
  • No linked Events
:
    {characterStory?.events.map(e =>
  • + props.changeTab('Events')} to={`/events/${e.id}`}>{e.name}
  • )} +
+ }
+ +
+ + Messages + {characterStory.messages.length < 1 ?
  • No messages
:
    {characterStory.messages.map(m =>
  • + props.changeTab('Messages')} to={`/messages/${m.id}`}>{m.name} - Sent: {m.sent}
  • )} +
+ }
+ +
+ + Summary + + {character_summary.length < 1 ?

No summary

:
    {character_summary.map(n =>
  • {n}
  • )} +
}
+ + + GM Notes + + + + + + + {gm_notes_list.length < 1 ?
  • No notes
: +
    {gm_notes_list.map(n =>
  • {n}
  • )}
}
+ +
+ + Family + + + {family_list.length ?
    {family_list}
:
  • None
} +
+ + Other known relations + + + {relations_list.length ?
    {relations_list}
:
  • None
}
+
+ + Personal + + + {personal_history_list ?
    {personal_history_list}
:
  • None
} +
+ + Classified personal data + + + {character.personal_secret_info ? character.personal_secret_info : "No classified personal data"} + + + Military + + + + Military Academies: + {military_academies_list ?
    {military_academies_list}
: "None"} + + Military Rank: {character.military_rank ? character.military_rank : "-"} +
+ + + Military Remarks:{!character.military_remarks && " None"} + {character.military_remarks &&
    {character.military_remarks.split('\n\n').filter(Boolean).map(r =>
  • {r}
  • )}
} + +
+ + Military Service History + + + {military_history_list?.length ?
    {military_history_list}
:
  • None
} +
+ + Medical + + + Fitness Level: {character.medical_fitness_level ? character.medical_fitness_level : "-"} + Blood Type: {character.medical_blood_type ? character.medical_blood_type : "-"} + Last Fitness Check: {character.medical_last_fitness_check ? character.medical_last_fitness_check : "-"} + + + + Active Conditions{!character.medical_active_conditions && : None} + {character.medical_active_conditions &&
    {character.medical_active_conditions.split('\n\n').filter(Boolean).map(c =>
  • {c}
  • )}
} + + + Current Medication{!character.medical_current_medication && : None} + {character.medical_current_medication &&
    {character.medical_current_medication.split('\n\n').filter(Boolean).map(m =>
  • {m}
  • )}
} + + + Allergies{!character.medical_allergies && : None} + {character.medical_allergies &&
    {character.medical_allergies.split(',').filter(Boolean).map(c =>
  • {c}
  • )}
} + +
+ + Medical History{medical_history.length === 0 && : None} + + + {medical_history_list ?
    {medical_history_list}
: "None"} +
+ + Groups/Roles + + {character.groups.length === 0 &&
  • None
} +
    {character.groups.map(group =>
  • {group}
  • )}
+ + Metadata + + + Is Character: {getIsCharacterText(character)} + Is Visible: {character.is_visible ? "Yes" : "No"} + + +   + +
+
+ setShowCharacterEdit(false)} + onEditDone={swrCharacter.mutate} + />
) } diff --git a/src/components/Event.js b/src/components/Event.js index 594ca17..2bef9ad 100644 --- a/src/components/Event.js +++ b/src/components/Event.js @@ -128,31 +128,6 @@ export default function Event(props) { GM Notes {gm_notes.length < 1 ?
  • No notes available
:
    {gm_notes.map(n =>
  • {n}
  • )}
}
- - GM Notes During the Runs [ADD NOTE BUTTON] [HIDE PREVIOUS RUNS CHECKBOX] - -
  • - Timestamp: Note 6 -
  • -
  • - Timestamp: Note 5 -
  • -
  • - Timestamp: Note 4 -
  • -
  • - Timestamp: Note 3 -
  • -
  • - Timestamp: Note 2 -
  • -
  • - Timestamp: Note 1 -
  • -
- - Save the notes between games! -   diff --git a/src/components/Message.css b/src/components/Message.css index bea8eed..536a5c2 100644 --- a/src/components/Message.css +++ b/src/components/Message.css @@ -65,3 +65,7 @@ h1.message { .splitscreen .right { flex: 0.2; } + +.warning { + color: var(--bs-danger); +} \ No newline at end of file diff --git a/src/components/Message.js b/src/components/Message.js index a5a94f2..eb3b818 100644 --- a/src/components/Message.js +++ b/src/components/Message.js @@ -63,7 +63,8 @@ export default function Messages(props) { ?
  • No sender
:
    {message.sender?.id &&
  • - props.changeTab('Characters')} to={`/characters/${message.sender?.id}`}>{message.sender?.name} - {message.sender?.is_character ? 'Character' : 'NPC'} - Card ID: {message.sender?.card_id} + props.changeTab('Characters')} to={`/characters/${message.sender?.id}`}>{message.sender?.name} - {message.sender?.is_character ? 'Character' : 'NPC'} - + Card ID: {message.sender?.card_id}{!['Deceased', 'Killed in action'].includes(message.sender?.status) ? '' : - {message.sender?.status.toUpperCase()}}
  • }
} @@ -132,7 +133,15 @@ export default function Messages(props) {
- +
diff --git a/src/components/Plot.js b/src/components/Plot.js index 72c5d05..6971267 100644 --- a/src/components/Plot.js +++ b/src/components/Plot.js @@ -106,31 +106,6 @@ export default function Plot(props) { {plot_notes.length < 1 ?
  • No notes available
:
    {plot_notes.map(n =>
  • {n}
  • )}
}
- - GM Notes During the Runs [ADD NOTE BUTTON] [HIDE PREVIOUS RUNS CHECKBOX] - -
  • - Timestamp: Note 6 -
  • -
  • - Timestamp: Note 5 -
  • -
  • - Timestamp: Note 4 -
  • -
  • - Timestamp: Note 3 -
  • -
  • - Timestamp: Note 2 -
  • -
  • - Timestamp: Note 1 -
  • -
- -

Save the notes between games!

-
Copied from characters diff --git a/src/components/modals/EditArtifactModal.js b/src/components/modals/EditArtifactModal.js new file mode 100644 index 0000000..f4a07f3 --- /dev/null +++ b/src/components/modals/EditArtifactModal.js @@ -0,0 +1,96 @@ +import React from "react"; +import Modal from 'react-bootstrap/Modal'; +import Form from 'react-bootstrap/Form'; +import { Button } from "react-bootstrap"; +import { useNavigate } from "react-router-dom"; +import { errorToast, successToast } from "../../utils/toaster"; +import { updateArtifactGmNotes } from "../../api/artifact"; + +const DEFAULT_ARTIFACT_STATE = { + gm_notes: '', +}; + +const EditArtifactModal = (props) => { + const { showModal, handleClose, onEditDone, characterToEdit: artifactToEdit } = props; + const [artifact, setArtifact] = React.useState(DEFAULT_ARTIFACT_STATE); + const [isSubmitting, setIsSubmitting] = React.useState(false); + const navigate = useNavigate(); + + React.useEffect(() => { + if (!artifactToEdit) { + return; + } + setArtifact(artifactToEdit); + }, [artifactToEdit]); + + const afterSubmit = (artifactId) => { + handleClose(false); + setArtifact(DEFAULT_ARTIFACT_STATE); + setIsSubmitting(false); + if (artifactToEdit) { + onEditDone(); + } + else { + navigate(`/artifacts/${artifactId}`); + } + }; + + const handleSubmit = async () => { + if (isSubmitting) { + return; + } + setIsSubmitting(true); + + const response = await updateArtifactGmNotes(artifact.id, artifact.gm_notes); + + if (response.ok) { + const data = await response.json(); + successToast('Artifact GM notes updated!'); + afterSubmit(data.id); + } else { + console.error(`Got HTTP ${response.status} response:`, await response.text()); + setIsSubmitting(false); + errorToast('Failed to update artifact'); + } + }; + + return ( + + + Edit {artifact?.catalog_id} - {artifact?.name} + + +
+ + GM Notes: + { + setArtifact({ ...artifact, gm_notes: evt.target.value }); + }} + /> + +
+
+ + + + +
+ ) +}; + +export default EditArtifactModal; \ No newline at end of file diff --git a/src/components/modals/EditCharacterModal.js b/src/components/modals/EditCharacterModal.js new file mode 100644 index 0000000..14fce89 --- /dev/null +++ b/src/components/modals/EditCharacterModal.js @@ -0,0 +1,96 @@ +import React from "react"; +import Modal from 'react-bootstrap/Modal'; +import Form from 'react-bootstrap/Form'; +import { Button } from "react-bootstrap"; +import { useNavigate } from "react-router-dom"; +import { errorToast, successToast } from "../../utils/toaster"; +import { updateCharacterGmNotes } from "../../api/character"; + +const DEFAULT_CHARACTER_STATE = { + gm_notes: '', +}; + +const EditCharacterModal = (props) => { + const { showModal, handleClose, onEditDone, characterToEdit } = props; + const [character, setCharacter] = React.useState(DEFAULT_CHARACTER_STATE); + const [isSubmitting, setIsSubmitting] = React.useState(false); + const navigate = useNavigate(); + + React.useEffect(() => { + if (!characterToEdit) { + return; + } + setCharacter(characterToEdit); + }, [characterToEdit]); + + const afterSubmit = (characterId) => { + handleClose(false); + setCharacter(DEFAULT_CHARACTER_STATE); + setIsSubmitting(false); + if (characterToEdit) { + onEditDone(); + } + else { + navigate(`/characters/${characterId}`); + } + }; + + const handleSubmit = async () => { + if (isSubmitting) { + return; + } + setIsSubmitting(true); + + const response = await updateCharacterGmNotes(character.id, character.gm_notes); + + if (response.ok) { + const data = await response.json(); + successToast('Character GM notes updated!'); + afterSubmit(data.id); + } else { + console.error(`Got HTTP ${response.status} response:`, await response.text()); + setIsSubmitting(false); + errorToast('Failed to update character'); + } + }; + + return ( + + + Edit {character?.full_name} + + +
+ + GM Notes: + { + setCharacter({ ...character, gm_notes: evt.target.value }); + }} + /> + +
+
+ + + + +
+ ) +}; + +export default EditCharacterModal; \ No newline at end of file