WIP feedback modal

This commit is contained in:
taskylizard 2023-11-02 00:35:41 +05:30
parent 11844cde39
commit ad89f51fb2
No known key found for this signature in database
GPG Key ID: 5CABA3D642DDC497
3 changed files with 274 additions and 0 deletions

View File

@ -0,0 +1,193 @@
<script setup lang="ts">
import { reactive, ref } from "vue";
interface Feedback {
message: string;
feedbackType?: string;
anonymous?: boolean;
contactEmail?: string;
}
const loading = ref(false);
const error = ref<unknown>(null);
const success = ref(false);
const feedback = reactive<Feedback>({ message: "" });
const feedbackOptions = [
{ label: "🐞 Bug", value: "bug" },
{
label: "♻️ Suggestion",
value: "suggestion",
},
{ label: "📂 Other", value: "other" },
{
label: "❤️ Appreciation",
value: "appreciate",
},
];
function getFeedbackOption(value: string) {
return feedbackOptions.find((option) => option.value === value);
}
async function handleSubmit(type?: string) {
if (type) feedback.feedbackType = type as string;
const body: Feedback = {
message: feedback.message,
feedbackType: feedback.feedbackType,
contactEmail: feedback.contactEmail,
anonymous: feedback.anonymous,
};
try {
const response = await fetch("/api/feedback", {
method: "POST",
body: JSON.stringify(body),
});
const data = await response.json();
if (data.error) {
error.value = data.error;
return;
}
success.value = true;
} catch (err) {
error.value = err;
} finally {
loading.value = false;
}
}
</script>
<template>
<div class="wrapper">
<Transition name="fade" mode="out-in">
<div v-if="!feedback.feedbackType" class="step">
<div>
<div>
<p class="heading">Feedback</p>
</div>
</div>
<div class="button-container">
<button v-for="item in feedbackOptions" :key="item.value" class="btn" @click="handleSubmit(item.value)">
<span>{{ item.label }}</span>
</button>
</div>
</div>
<div v-else-if="feedback.feedbackType && !success" class="step">
<div>
<p class="desc">The wiki is...</p>
<div>
<span>{{ getFeedbackOption(feedback.feedbackType)?.label }}</span>
<button style="margin-left: 0.5rem" class="btn" @click="feedback.feedbackType = undefined">
<span class="i-carbon-close">close</span>
</button>
</div>
</div>
<textarea v-model="feedback.message" autofocus class="input" />
<button class="btn btn-primary" :disabled="!feedback.message" @click="handleSubmit()">
Send us your feedback
</button>
</div>
<div v-else class="step">
<p class="heading">Thanks for your feedback!</p>
</div>
</Transition>
</div>
</template>
<style scoped>
.step>*+* {
margin-top: 1rem;
}
.btn {
border: 1px solid var(--vp-c-divider);
background-color: var(--vp-c-bg);
border-radius: 8px;
transition:
border-color 0.25s,
background-color 0.25s;
display: inline-block;
font-size: 14px;
font-weight: 500;
line-height: 1.5;
margin: 0;
padding: 0.375rem 0.75rem;
text-align: center;
vertical-align: middle;
white-space: nowrap;
}
.btn:disabled {
opacity: 0.5;
}
.btn:hover {
border-color: var(--vp-c-brand);
}
.btn-primary {
color: #fff;
background-color: var(--vp-c-brand);
border-color: var(--vp-c-brand);
}
.btn-primary:hover {
background-color: var(--vp-c-brand-darker);
border-color: var(--vp-c-brand-darker);
}
.heading {
font-size: 1.2rem;
font-weight: 700;
}
.button-container {
display: grid;
grid-gap: 0.5rem;
}
.wrapper {
margin: 2rem 0;
padding: 1.5rem;
border: 1px solid var(--vp-c-divider);
border-radius: 8px;
background: var(--vp-c-bg-alt);
}
.input {
width: 100%;
height: 100px;
border: 1px solid #ccc;
border-radius: 4px;
padding: 0.375rem 0.75rem;
}
.desc {
display: block;
line-height: 20px;
font-size: 12px;
font-weight: 500;
color: var(--vp-c-text-2);
}
@media screen and (min-width: 768px) {
.button-container {
grid-template-columns: repeat(4, 1fr);
}
}
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.25s ease;
}
.fade-enter-from,
.fade-leave-to {
opacity: 0;
}
</style>

View File

@ -0,0 +1,79 @@
<script setup lang="ts">
import { ref } from "vue";
import Feedback from "./Feedback.vue";
const showModal = ref(false);
</script>
<template>
<button class="modal-button" @click="showModal = true">Show Modal</button>
<Teleport to="body">
<Transition name="modal">
<div v-show="showModal" class="modal-mask">
<div class="modal-container">
<p>Send Feedback</p>
<Feedback />
<div class="model-footer">
<button class="modal-button" @click="showModal = false">Close</button>
</div>
</div>
</div>
</Transition>
</Teleport>
</template>
<style scoped>
.modal-mask {
position: fixed;
z-index: 200;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
transition: opacity 0.3s ease;
}
.modal-container {
width: 300px;
margin: auto;
padding: 20px 30px;
background-color: var(--vp-c-bg);
border-radius: 2px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.33);
transition: all 0.3s ease;
}
.model-footer {
margin-top: 8px;
text-align: right;
}
.modal-button {
padding: 4px 8px;
border-radius: 4px;
border-color: var(--vp-button-alt-border);
color: var(--vp-button-alt-text);
background-color: var(--vp-button-alt-bg);
}
.modal-button:hover {
border-color: var(--vp-button-alt-hover-border);
color: var(--vp-button-alt-hover-text);
background-color: var(--vp-button-alt-hover-bg);
}
.modal-enter-from,
.modal-leave-to {
opacity: 0;
}
.modal-enter-from .modal-container,
.modal-leave-to .modal-container {
transform: scale(1.1);
}
</style>

View File

@ -1,5 +1,6 @@
<script setup lang="ts">
import Field from "./PaneFields.vue";
import Modal from "./Modal.vue"
</script>
<template>
@ -10,6 +11,7 @@ import Field from "./PaneFields.vue";
<Field icon="i-twemoji-star">Community Recommendations</Field>
<Field icon="i-twemoji-globe-with-meridians">3rd Party Indexes</Field>
<Field icon="i-twemoji-repeat-button">Storage Page Links</Field>
<Modal />
</div>
</template>