Skip to content

Commit

Permalink
Admin UI (#114)
Browse files Browse the repository at this point in the history
Co-authored-by: David O'Brien <[email protected]>
Co-authored-by: Ross Newman <[email protected]>
  • Loading branch information
3 people authored Sep 22, 2024
1 parent 3f1a1e1 commit 189e172
Show file tree
Hide file tree
Showing 12 changed files with 274 additions and 126 deletions.
3 changes: 3 additions & 0 deletions packages/ui/public/material-symbols_upload.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions packages/ui/public/mdi_pencil.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 10 additions & 0 deletions packages/ui/public/pepicons-pencil_dots-y.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion packages/ui/src/components/AppNavbar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ const isCollapsed = computed(() => {
<ElImage src="/icons/pencil.svg" />
<template v-if="!isCollapsed">Task Templates</template>
</ElMenuItem>
<ElMenuItem class="flex items-center gap-3">
<ElMenuItem index="/admin/users" class="flex items-center gap-3">
<ElImage src="/icons/people.svg" />
<template v-if="!isCollapsed">Users</template>
</ElMenuItem>
Expand Down
4 changes: 2 additions & 2 deletions packages/ui/src/components/ui/AppButton.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ defineEmits(["click"]);
</script>

<template>
<button
<el-button
class="bg-primary-300 text-white border border-primary-400 rounded-lg px-5 py-1 justify-center"
@click="$emit('click')"
>
<slot />
</button>
</el-button>
</template>
107 changes: 9 additions & 98 deletions packages/ui/src/pages/AdminPage/AdminPage.vue
Original file line number Diff line number Diff line change
@@ -1,115 +1,26 @@
<script setup lang="ts">
import { ref } from "vue";
import { useRouter } from "vue-router";
import { useAuthStore } from "@/stores/auth";
import { useMutation, useQuery, useQueryClient } from "@tanstack/vue-query";
import taskAPI from "@/api/tasks";
import adminAPI from "@/api/admin";
import AppButton from "@/components/ui/AppButton.vue";
import { ElNotification } from "element-plus";
import AppEditModal from "@/components/ui/AppEditModal.vue";
import AdminTaskLibrary from "./components/AdminTaskLibrary.vue";
import BatteryEditForm from "./components/BatteryEditForm.vue";
import { useBatteryEditingStore } from "../../stores/admin";
const router = useRouter();
const authStore = useAuthStore();
const queryClient = useQueryClient();
const batteryEditingStore = useBatteryEditingStore();
const name = ref("");
if (!authStore.currentUser?.isAdmin) {
router.push("/");
}
const deleteMutation = useMutation(taskAPI.deleteTask, {
onSuccess: () => {
queryClient.invalidateQueries(["tasks"]);
ElNotification({
title: "Success",
message: "Battery deleted successfully",
type: "success",
});
},
});
const promoteUser = useMutation(adminAPI.promoteUserToAdmin, {});
const uploadMutation = useMutation(taskAPI.uploadBattery, {
onSuccess: () => {
queryClient.invalidateQueries(["tasks"]);
ElNotification({
title: "Success",
message: "Battery uploaded successfully",
type: "success",
});
},
onError: () => {
ElNotification({
title: "Error",
message: "There was an error uploading the battery",
type: "error",
});
},
});
const { data } = useQuery({
queryKey: ["tasks"],
queryFn: taskAPI.getAllTasks,
});
function handleFileUpload(event: Event) {
const target = event.target as HTMLInputElement;
const file: File = (target.files as FileList)[0];
const reader = new FileReader();
reader.readAsText(file, "UTF-8");
reader.onload = (readerEvent) => {
const content = readerEvent.target?.result as string;
const parsedContent = JSON.parse(content);
uploadMutation.mutate(parsedContent);
};
}
</script>
<template>
<h1 class="text-2xl">Batteries</h1>
<input
id="upload-file"
type="file"
name="upload-file"
accept=".json"
hidden
@change="handleFileUpload"
/>
<label
for="upload-file"
refs="upload-file"
class="bg-blue-500 text-white px-4 py-2 rounded-md cursor-pointer"
>
Upload File</label
>
<div v-for="task in data" :key="task._id" class="border border-black p-4">
<h2 class="text-xl">{{ task.name }}</h2>
<p>{{ task.description }}</p>
<AppButton @click="batteryEditingStore.select(task._id)">Edit</AppButton>
<AppButton @click="deleteMutation.mutate(task._id)">
Delete Me! 😲
</AppButton>
</div>
<AppEditModal
:visible="batteryEditingStore.editingBatteryId !== undefined"
header="Edit Battery"
sub-header="Customize your task's default values"
@cancel="batteryEditingStore.editingBatteryId = undefined"
@done="batteryEditingStore.editingBatteryId = undefined"
>
<BatteryEditForm />
</AppEditModal>
<div class="mt-3">
<h1 class="text-2xl">Promote User to Admin</h1>
<ElFormItem label="Email">
<ElInput v-model="name" />
</ElFormItem>

<AppButton @click="promoteUser.mutate(name)">Promote</AppButton>
<div class="flex h-screen items-stretch">
<AdminTaskLibrary class="flex-none" />
<div
v-if="batteryEditingStore.editingBatteryId !== undefined"
class="flex-1 mx-6 my-8 p-8 rounded-2xl min-w-[600px] bg-white shadow-xl overflow-y-hidden"
>
<BatteryEditForm />
</div>
</div>
</template>
120 changes: 120 additions & 0 deletions packages/ui/src/pages/AdminPage/components/AdminTaskLibrary.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
<script setup lang="ts">
import { useMutation, useQuery, useQueryClient } from "@tanstack/vue-query";
import taskAPI from "@/api/tasks";
import { ElNotification } from "element-plus";
import { useBatteryEditingStore } from "../../../stores/admin.ts";
// const router = useRouter();
// const authStore = useAuthStore();
const queryClient = useQueryClient();
const batteryEditingStore = useBatteryEditingStore();
// if (!authStore.currentUser?.isAdmin) {
// router.push("/");
// }
// const deleteMutation = useMutation(taskAPI.deleteTask, {
// onSuccess: () => {
// queryClient.invalidateQueries(["tasks"]);
// ElNotification({
// title: "Success",
// message: "Battery deleted successfully",
// type: "success",
// });
// },
// });
const uploadMutation = useMutation(taskAPI.uploadBattery, {
onSuccess: () => {
queryClient.invalidateQueries(["tasks"]);
ElNotification({
title: "Success",
message: "Battery uploaded successfully",
type: "success",
});
},
onError: () => {
ElNotification({
title: "Error",
message: "There was an error uploading the battery",
type: "error",
});
},
});
const { data } = useQuery({
queryKey: ["tasks"],
queryFn: taskAPI.getAllTasks,
});
function handleFileUpload(event: Event) {
const target = event.target as HTMLInputElement;
const file: File = (target.files as FileList)[0];
const reader = new FileReader();
reader.readAsText(file, "UTF-8");
reader.onload = (readerEvent) => {
const content = readerEvent.target?.result as string;
const parsedContent = JSON.parse(content);
uploadMutation.mutate(parsedContent);
};
}
</script>
<template>
<div class="w-[380px] px-8 py-9 rounded-r-2xl h-full bg-white shadow-2xl">
<div class="flex flex-col gap-8 h-full">
<h1 class="text-xl font-bold">Task Template Library</h1>
<div
class="flex py-2 ml-20 justify-center text-black w-36 border border-black rounded-xl font-bold"
>
<input
id="upload-file"
type="file"
name="upload-file"
accept=".json"
hidden
@change="handleFileUpload"
/>
<label for="upload-file" refs="upload-file" class="flex cursor-pointer">
<ElImage src="/material-symbols_upload.svg" />
Upload File</label
>
</div>
<ElScrollbar>
<div class="flex-1 flex flex-col gap-4">
<div v-for="task in data" :key="task._id">
<div
:class="[
'flex gap-5 p-4 border rounded-2xl cursor-pointer',
batteryEditingStore.editingBatteryId === task._id
? 'bg-neutral-100 border-neutral-400'
: 'bg-neutral-10 border-neutral-300',
]"
@click="batteryEditingStore.select(task._id)"
>
<ElImage
:src="task.imageUrl"
fit="cover"
class="flex-none h-24 w-24 rounded-[10px] overflow-hidden"
/>
<div class="flex-1 flex flex-col gap-3 overflow-hidden">
<div class="flex">
<div class="text-sm text-neutral-600 truncate font-bold">
{{ task.name }}
</div>
<div class="grow"></div>
<ElImage
src="/pepicons-pencil_dots-y.svg"
class="h-5 pl-2 cursor-pointer"
/>
</div>
<div class="text-xs text-neutral-600 font-medium line-clamp-4">
{{ task.description }}
</div>
</div>
</div>
</div>
</div>
</ElScrollbar>
</div>
</div>
</template>
Loading

0 comments on commit 189e172

Please sign in to comment.