Skip to content

Commit 9412bc3

Browse files
authored
feat: admin page to view and edit announcements (#218)
- add titles to admin edit pages - add headers to admin edit page input boxes
1 parent 8eae7a3 commit 9412bc3

File tree

10 files changed

+270
-11
lines changed

10 files changed

+270
-11
lines changed

CONTRIBUTING.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -132,9 +132,9 @@ To define new translation keys yourself, contact us on the Discord about getting
132132

133133
See these documentation pages about how to use Tolgee in code:
134134

135-
- [Using `$t()` and the `T` component](https://tolgee.io/js-sdk/integrations/svelte/translating)
136-
- [Full spec for `$t()`](https://tolgee.io/js-sdk/api/core_package/tolgee#t)
137-
- [ICU Message Format](https://tolgee.io/platform/translation_process/icu_message_format)
135+
* [Using `$t()` and the `T` component](https://tolgee.io/js-sdk/integrations/svelte/translating)
136+
* [Full spec for `$t()`](https://tolgee.io/js-sdk/api/core_package/tolgee#t)
137+
* [ICU Message Format](https://tolgee.io/platform/translation_process/icu_message_format)
138138

139139
### Downloading Translations
140140

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
mutation CreateAnnouncement($announcement: NewAnnouncement!) {
2+
createAnnouncement(announcement: $announcement) {
3+
id
4+
message
5+
importance
6+
}
7+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
mutation DeleteAnnouncement($id: AnnouncementID!) {
2+
deleteAnnouncement(announcementId: $id)
3+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
mutation UpdateAnnouncement($id: AnnouncementID!, $announcement: UpdateAnnouncement!) {
2+
updateAnnouncement(announcementId: $id, announcement: $announcement) {
3+
id
4+
message
5+
importance
6+
}
7+
}

src/routes/admin/+page.svelte

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@
2626
<a href="{base}/admin/tag-manager" class="text-white">
2727
<h2>Tag Manager</h2>
2828
</a>
29+
30+
<a href="{base}/admin/announcements" class="text-white">
31+
<h2>Announcements</h2>
32+
</a>
2933
</section>
3034
</div>
3135

Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
<script lang="ts">
2+
import { getContextClient, queryStore } from '@urql/svelte';
3+
import {
4+
GetAnnouncementsDocument,
5+
CreateAnnouncementDocument,
6+
type Announcement,
7+
AnnouncementImportance,
8+
UpdateAnnouncementDocument,
9+
DeleteAnnouncementDocument
10+
} from '$lib/generated';
11+
import { Accordion, AccordionItem, getToastStore } from '@skeletonlabs/skeleton';
12+
const client = getContextClient();
13+
14+
let announcements: Announcement[] = [];
15+
const nameFields = {};
16+
let announcementNegativeID = -1;
17+
const defaultNewAnnouncementMessage = 'New Announcement';
18+
19+
const annQuery = queryStore({
20+
query: GetAnnouncementsDocument,
21+
client
22+
});
23+
24+
$: announcements = $annQuery.data?.getAnnouncements || [];
25+
26+
const toastStore = getToastStore();
27+
28+
function newAnnouncement() {
29+
if (!announcements.find((announcement) => announcement.message == defaultNewAnnouncementMessage)) {
30+
const announcement = {
31+
id: announcementNegativeID--,
32+
message: defaultNewAnnouncementMessage,
33+
importance: AnnouncementImportance.Info
34+
} as Announcement;
35+
announcements.push(announcement);
36+
announcements = announcements;
37+
setTimeout(() => {
38+
const field = nameFields[announcement.id];
39+
field.focus();
40+
const input = field.getElement().querySelectorAll('input')[0] as HTMLInputElement;
41+
input.select();
42+
}, 0);
43+
} else {
44+
nameFields[announcements[announcements.length - 1].id].focus();
45+
}
46+
}
47+
48+
async function announcementChange(announcement: Announcement) {
49+
if (announcement.message == defaultNewAnnouncementMessage) {
50+
return;
51+
}
52+
53+
let success = false;
54+
if (announcement.id < 0) {
55+
// Create new tag & update tag.id with new DB id or re-fetch all tags
56+
try {
57+
const result = await client
58+
.mutation(CreateAnnouncementDocument, {
59+
announcement: { importance: announcement.importance, message: announcement.message }
60+
})
61+
.toPromise();
62+
if (result.data) {
63+
announcement.id = result.data.createAnnouncement.id;
64+
success = true;
65+
}
66+
} catch (err) {
67+
console.log(err);
68+
}
69+
if (!success) {
70+
toastStore.trigger({
71+
message: `Failed to create Announcement '${announcement.message}'!`,
72+
background: 'variant-filled-error',
73+
timeout: 2000
74+
});
75+
return;
76+
}
77+
} else {
78+
// Update existing tag
79+
try {
80+
if (announcement.message.length > 0) {
81+
success =
82+
(
83+
await client
84+
.mutation(UpdateAnnouncementDocument, {
85+
id: announcement.id,
86+
announcement: { importance: announcement.importance, message: announcement.message }
87+
})
88+
.toPromise()
89+
).data.updateAnnouncement != null;
90+
}
91+
} catch {
92+
// nothing
93+
}
94+
if (!success) {
95+
toastStore.trigger({
96+
message: `Failed to update Announcement '${announcement.message}'!`,
97+
background: 'variant-filled-error',
98+
timeout: 2000
99+
});
100+
return;
101+
}
102+
}
103+
104+
toastStore.trigger({
105+
message: `Announcement '${announcement.message}' saved!`,
106+
background: 'variant-filled-success',
107+
timeout: 2000
108+
});
109+
}
110+
111+
async function deleteAnnouncement(announcement: Announcement) {
112+
if (announcement.message != defaultNewAnnouncementMessage) {
113+
let success = false;
114+
try {
115+
const result = await client.mutation(DeleteAnnouncementDocument, { id: announcement.id }).toPromise();
116+
success = result.data.deleteAnnouncement;
117+
} catch {
118+
success = false;
119+
}
120+
if (!success) {
121+
toastStore.trigger({
122+
message: `Failed to remove Announcement '${announcement.message}'!`,
123+
background: 'variant-filled-error',
124+
timeout: 2000
125+
});
126+
return;
127+
}
128+
} else {
129+
announcements.splice(announcements.indexOf(announcement), 1);
130+
}
131+
132+
toastStore.trigger({
133+
message: `Tag '${announcement.message}' removed!`,
134+
background: 'variant-filled-success',
135+
timeout: 2000
136+
});
137+
}
138+
139+
function onDeleteClick(e: Event, announcement: Announcement) {
140+
e.stopPropagation();
141+
deleteAnnouncement(announcement);
142+
}
143+
</script>
144+
145+
<h1>Announcements</h1>
146+
147+
<div class="card">
148+
{#if $annQuery.fetching}
149+
<h1>Loading announcements...</h1>
150+
{:else if $annQuery.error}
151+
<h1>Failed to load announcements: {$annQuery.error.message}</h1>
152+
{:else}
153+
<Accordion>
154+
{#each announcements as announcement}
155+
<AccordionItem>
156+
<svelte:fragment slot="summary"
157+
>({announcement.importance}) {(() => {
158+
const trimTo = 144;
159+
const trimmed = announcement.message.substring(0, trimTo);
160+
return announcement.message.length > trimTo ? trimmed + '...' : trimmed;
161+
})()}</svelte:fragment>
162+
<svelte:fragment slot="content">
163+
<div>
164+
<div>Message</div>
165+
<input
166+
type="text"
167+
class="input p-2"
168+
bind:value={announcement.message}
169+
placeholder="This is the text of the announcement"
170+
bind:this={nameFields[announcement.id]}
171+
on:change={() => announcementChange(announcement)} />
172+
<div>Importance (Fix, Info, Warning, Alert)</div>
173+
<input
174+
type="text"
175+
class="input p-2"
176+
bind:value={announcement.importance}
177+
placeholder="Importance level"
178+
on:change={() => announcementChange(announcement)} />
179+
</div>
180+
181+
<button class="variant-ghost-error btn" on:click={(e) => onDeleteClick(e, announcement)}>
182+
<span>Delete</span>
183+
</button>
184+
</svelte:fragment>
185+
</AccordionItem>
186+
{/each}
187+
</Accordion>
188+
189+
<section class="p-4">
190+
<button class="variant-ghost-primary btn" on:click={newAnnouncement}>
191+
<span>Add new announcement</span>
192+
<span class="material-icons">add</span>
193+
</button>
194+
</section>
195+
{/if}
196+
</div>
197+
198+
<style lang="postcss">
199+
h1 {
200+
@apply my-4 text-4xl font-bold;
201+
}
202+
</style>

src/routes/admin/satisfactory-versions/+page.svelte

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,8 @@
150150
}
151151
</script>
152152

153+
<h1>Satisfactory Versions</h1>
154+
153155
<div class="card">
154156
{#if $satisfactoryVersionsQuery.fetching}
155157
<h1>Loading satisfactory versions...</h1>
@@ -162,14 +164,15 @@
162164
<svelte:fragment slot="summary">{satisfactoryVersion.version}</svelte:fragment>
163165
<svelte:fragment slot="content">
164166
<div>
167+
<div>Game CL#</div>
165168
<input
166169
type="text"
167170
class="input p-2"
168171
bind:value={satisfactoryVersion.version}
169172
placeholder="Version"
170173
bind:this={versionFields[satisfactoryVersion.id]}
171174
on:change={() => satisfactoryVersionChange(satisfactoryVersion)} />
172-
175+
<div>Engine Version</div>
173176
<input
174177
type="text"
175178
class="input p-2"
@@ -194,3 +197,9 @@
194197
</section>
195198
{/if}
196199
</div>
200+
201+
<style lang="postcss">
202+
h1 {
203+
@apply my-4 text-4xl font-bold;
204+
}
205+
</style>

src/routes/admin/tag-manager/+page.svelte

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
const panels = {};
1111
const nameFields = {};
1212
let tagNegativeID = -1;
13+
const defaultNewTagName = 'New Tag';
1314
1415
const tagsQuery = queryStore({
1516
query: GetTagsDocument,
@@ -24,8 +25,8 @@
2425
const toastStore = getToastStore();
2526
2627
function newTag() {
27-
if (!tags.find((tag) => tag.name == 'New Tag')) {
28-
const tag = { id: tagNegativeID--, name: 'New Tag', description: 'Description' } as Tag;
28+
if (!tags.find((tag) => tag.name == defaultNewTagName)) {
29+
const tag = { id: tagNegativeID--, name: defaultNewTagName, description: 'Description' } as Tag;
2930
tags.push(tag);
3031
tags = tags;
3132
setTimeout(() => {
@@ -42,8 +43,7 @@
4243
}
4344
4445
async function tagChange(tag: Tag) {
45-
// ignore "New Tag"
46-
if (tag.name == 'New Tag') {
46+
if (tag.name == defaultNewTagName) {
4747
return;
4848
}
4949
@@ -99,7 +99,7 @@
9999
}
100100
101101
async function deleteTag(tag: Tag) {
102-
if (tag.name != 'New Tag') {
102+
if (tag.name != defaultNewTagName) {
103103
// Remove tag
104104
let success = false;
105105
try {
@@ -116,6 +116,8 @@
116116
});
117117
return;
118118
}
119+
} else {
120+
tags.splice(tags.indexOf(tag), 1);
119121
}
120122
121123
toastStore.trigger({
@@ -131,6 +133,8 @@
131133
}
132134
</script>
133135

136+
<h1>Mod Tag Manager</h1>
137+
134138
<div class="card">
135139
{#if $tagsQuery.fetching}
136140
<h1>Loading tags...</h1>
@@ -139,18 +143,19 @@
139143
{:else}
140144
<Accordion>
141145
{#each tags as tag}
142-
<AccordionItem>
146+
<AccordionItem bind:this={panels[tag.id]}>
143147
<svelte:fragment slot="summary">{tag.name}</svelte:fragment>
144148
<svelte:fragment slot="content">
145149
<div>
150+
<div>Tag name</div>
146151
<input
147152
type="text"
148153
class="input p-2"
149154
bind:value={tag.name}
150155
placeholder="Name"
151156
bind:this={nameFields[tag.id]}
152157
on:change={() => tagChange(tag)} />
153-
158+
<div>Tag description</div>
154159
<input
155160
type="text"
156161
class="input p-2"
@@ -176,3 +181,9 @@
176181
</section>
177182
{/if}
178183
</div>
184+
185+
<style lang="postcss">
186+
h1 {
187+
@apply my-4 text-4xl font-bold;
188+
}
189+
</style>

src/routes/admin/unapproved-mods/+page.svelte

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@
5656
<MetaDescriptors description="Unapproved mods" title="Admin: Unapproved Mods" />
5757
</svelte:head>
5858

59+
<h1>Unapproved Mods</h1>
60+
5961
{#if totalMods}
6062
<div class="mb-5 ml-auto flex justify-end">
6163
<div>
@@ -118,3 +120,9 @@
118120
</div>
119121
</div>
120122
{/if}
123+
124+
<style lang="postcss">
125+
h1 {
126+
@apply my-4 text-4xl font-bold;
127+
}
128+
</style>

0 commit comments

Comments
 (0)