Skip to content

Commit

Permalink
feat: Add ButtonInput component
Browse files Browse the repository at this point in the history
- Import ButtonInput.vue component from the components/FormKit folder.
- Create a new input called "inputButton" and assign the ButtonInput component to it.
  • Loading branch information
realashleybailey committed Sep 27, 2023
1 parent a4db20c commit 976615c
Show file tree
Hide file tree
Showing 7 changed files with 159 additions and 34 deletions.
4 changes: 4 additions & 0 deletions frontend/src/formkit.config.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { createProPlugin, inputs } from "@formkit/pro";

import ButtonInput from "./components/FormKit/ButtonInput.vue";
import type { DefaultConfigOptions } from "@formkit/vue";
import OneTimePassword from "./components/FormKit/OneTimePassword.vue";
import { createInput } from "@formkit/vue";
Expand Down Expand Up @@ -35,6 +36,9 @@ const config: DefaultConfigOptions = {
otp: createInput(OneTimePassword, {
props: ["digits"],
}),
inputButton: createInput(ButtonInput, {
type: "input",
}),
},
};

Expand Down
21 changes: 19 additions & 2 deletions frontend/src/formkit.theme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,15 @@ const theme: Record<string, Record<string, string>> = {
suffixIcon: "peer absolute inset-y-0 right-0 flex items-center pr-3 pointer-events-none bg-gray-50 dark:bg-gray-700 rounded-r border-r border-t border-b border-gray-300 dark:border-gray-600",
},

inputButton: {
input: "peer-[.formkit-prefix-icon]:pl-9 peer-[.formkit-suffix-icon]:pr-9 mb-1 w-full bg-gray-50 dark:bg-gray-700 text-gray-900 dark:text-white sm:text-sm border border-gray-300 dark:border-gray-600 rounded block w-full dark:placeholder-gray-400 focus:ring-primary focus:border-primary",
label: "block mb-2 text-sm font-medium text-gray-900 dark:text-white",
inner: "w-full relative",
outer: "mb-4 formkit-disabled:opacity-50",
prefixIcon: "peer absolute inset-y-0 left-0 flex items-center pl-3 pointer-events-none bg-gray-50 dark:bg-gray-700 rounded-l border-l border-t border-b border-gray-300 dark:border-gray-600",
suffixIcon: "peer absolute inset-y-0 right-0 flex items-center pr-3 pointer-events-none bg-gray-50 dark:bg-gray-700 rounded-r border-r border-t border-b border-gray-300 dark:border-gray-600",
},

email: {
input: "peer-[.formkit-prefix-icon]:pl-9 peer-[.formkit-suffix-icon]:pr-9 mb-1 w-full bg-gray-50 dark:bg-gray-700 text-gray-900 dark:text-white sm:text-sm border border-gray-300 dark:border-gray-600 rounded block w-full dark:placeholder-gray-400 focus:ring-primary focus:border-primary",
label: "block mb-2 text-sm font-medium text-gray-900 dark:text-white",
Expand Down Expand Up @@ -120,8 +129,16 @@ const theme: Record<string, Record<string, string>> = {
},

"family:button": {
input: "$reset inline-flex items-center bg-primary hover:bg-primary_hover focus:outline-none text-white font-medium rounded dark:bg-primary dark:hover:bg-primary_hover px-5 py-2.5 text-sm formkit-disabled:bg-gray-400 formkit-loading:before:w-4 formkit-loading:before:h-4 formkit-loading:before:mr-2 formkit-loading:before:border formkit-loading:before:border-2 formkit-loading:before:border-r-transparent formkit-loading:before:rounded-3xl formkit-loading:before:border-white formkit-loading:before:animate-spin",
// wrapper: "mb-1",
input: `
bg-primary hover:bg-primary_hover
data-[theme=primary]:bg-primary data-[theme=primary]:hover:bg-primary_hover
data-[theme=secondary]:bg-secondary data-[theme=secondary]:hover:bg-secondary_hover
data-[theme=danger]:bg-red-600 data-[theme=danger]:hover:bg-red-500
data-[theme=success]:bg-green-500 data-[theme=success]:hover:bg-green-600
data-[theme=warning]:bg-yellow-500 data-[theme=warning]:hover:bg-yellow-600
inline-flex items-center focus:outline-none text-white font-medium rounded px-5 py-2.5 text-sm
`,
prefixIcon: "$reset block w-4 mr-2 stretch",
suffixIcon: "$reset block w-4 ml-2 stretch",
},
Expand Down
6 changes: 1 addition & 5 deletions frontend/src/modules/setup/router/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,8 @@ import type { RouteRecordRaw } from "vue-router";
const routes: Readonly<RouteRecordRaw[]> = [
{
path: "/setup",
redirect: "/setup/welcome",
},
{
path: "/setup/:step",
name: "setup",
component: () => import("@/views/SetupViews/SetupView.vue"),
component: () => import("../views/Setup.vue"),
},
];

Expand Down
38 changes: 35 additions & 3 deletions frontend/src/plugins/ModalWrapper.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,44 @@
import type { CustomModalOptions } from "./modal";
import type { CustomModalOptions, CustomModalOptionsButtons } from "./modal";

import { FormKit } from "@formkit/vue";
import { Modal } from "jenesius-vue-modal";
import type { WrapComponent } from "jenesius-vue-modal/dist/types/types/types";
import { defineComponent } from "vue";
import mitt from "mitt";

const ModalWrapper = <P extends WrapComponent>(component: P | string, props?: any, options?: Partial<CustomModalOptions>) => {
return defineComponent({
name: "ModalWrapper",
data() {
return {
eventBus: mitt(),
};
},
computed: {
attrs() {
// Create a shallow copy of the props and $attrs objects
const localAttrs = { ...props, ...this.$attrs, eventBus: this.eventBus };

// Iterate over the button actions and add a new property to the test object for each action
options?.actions?.forEach((action) => {
const alphaKey = action.event.replace(/([A-Z])/g, "-$1").toLowerCase();
const key = `on${alphaKey.charAt(0).toUpperCase() + alphaKey.slice(1)}`;
localAttrs[key] = action.callback ?? (() => {});
});

// Add custom close action
localAttrs.onClose = () => this.$emit(Modal.EVENT_PROMPT, false);

// Return the test object
return localAttrs;
},
},
methods: {
buttonTrigger(button: CustomModalOptionsButtons) {
button.onClick?.();
if (button.emit) this.eventBus.emit(button.emit);
},
},
render() {
return (
<div class="flex flex-col fixed top-0 bottom-0 left-0 right-0 h-full w-full md:h-auto md:w-auto transform text-left shadow-xl transition-all md:relative md:min-w-[30%] md:max-w-2xl md:shadow-none md:transform-none sm:align-middle text-gray-900 dark:text-white">
Expand All @@ -28,13 +60,13 @@ const ModalWrapper = <P extends WrapComponent>(component: P | string, props?: an
{/* Body */}
<div class="bg-white p-6 dark:bg-gray-800 p-6 space-y-6 flex-grow">
{/* String or Component */}
{typeof component === "string" ? <p>{component}</p> : <component {...props} />}
{typeof component === "string" ? <p>{component}</p> : <component {...this.attrs} />}
</div>
{/* Footer */}
{!options?.disableFooter ? (
<div class="flex items-center justify-end bg-white p-6 dark:bg-gray-800 p-6 space-x-2 border-t border-gray-200 dark:border-gray-600 rounded-b">
{options?.buttons?.map((button) => (
<FormKit type="button" classes={button.classes as any} onClick={() => button.onClick!} key={button.text}>
<FormKit type="button" classes={button.classes as any} onClick={() => this.buttonTrigger(button)} key={button.text}>
{button.text}
</FormKit>
))}
Expand Down
16 changes: 15 additions & 1 deletion frontend/src/plugins/modal.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { EventType, Handler } from "mitt";
import { Modal, closeById, closeModal, config, getComponentFromStore, getCurrentModal, modalQueue, onBeforeModalClose, openModal, popModal, promptModal, pushModal, useModalRouter } from "jenesius-vue-modal";

import type { App } from "vue";
Expand All @@ -7,6 +8,18 @@ import type { ModalOptions } from "jenesius-vue-modal/dist/types/utils/Modal";
import ModalWrapper from "./ModalWrapper";
import type { WrapComponent } from "jenesius-vue-modal/dist/types/types/types";

export declare interface CustomModalOptionsButtons {
text: string;
classes?: Record<string, string | Record<string, boolean> | FormKitClasses>;
onClick?: () => void;
emit?: string;
}

export declare interface CustomModalOptionsActions {
event: string;
callback: (options: Partial<CustomModalOptions>) => void;
}

export declare interface CustomModalOptions extends Partial<ModalOptions> {
title?: string;
disableHeader?: boolean;
Expand All @@ -16,7 +29,8 @@ export declare interface CustomModalOptions extends Partial<ModalOptions> {
confirmButtonText?: string;
disableCancelButton?: boolean;
cancelButtonText?: string;
buttons?: Array<Partial<{ text: string; classes: Record<string, string | Record<string, boolean> | FormKitClasses>; onClick: () => void }>>;
buttons?: CustomModalOptionsButtons[];
actions?: CustomModalOptionsActions[];
}

const localOpenModal = async <P extends WrapComponent>(component: P | string, options?: Partial<CustomModalOptions>, props?: any): Promise<Modal> => {
Expand Down
7 changes: 4 additions & 3 deletions frontend/src/plugins/sentry.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { BrowserTracing, Replay, init, vueRouterInstrumentation } from "@sentry/vue";
import DefaultToast from "@/components/Toasts/DefaultToast.vue";

import type { Options, TracingOptions } from "@sentry/vue/types/types";

import type { App } from "vue";
import DefaultToast from "@/components/Toasts/DefaultToast.vue";

type SentryOptions = Partial<
Omit<Options, "tracingOptions"> & {
Expand All @@ -13,6 +13,7 @@ type SentryOptions = Partial<
const vuePluginSentry = {
install: (app: App, options?: SentryOptions) => {
init({
app: app,
dsn: "https://d1994be8f88578e14f1a4ac06ae65e89@o4505748808400896.ingest.sentry.io/4505780347666432",
integrations: [
new BrowserTracing({
Expand All @@ -27,7 +28,7 @@ const vuePluginSentry = {
],
environment: process.env.NODE_ENV,
tracesSampleRate: 1.0,
replaysSessionSampleRate: 1.0,
replaysSessionSampleRate: 0,
replaysOnErrorSampleRate: 1.0,
beforeSend(event, hint) {
if (event.exception) {
Expand Down
101 changes: 81 additions & 20 deletions frontend/src/stores/libraries.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
import axios from "@/ts/utils/axios";

import { errorToast } from "@/ts/utils/toasts";
import { defineStore } from "pinia";

export const useLibrariesStore = defineStore("libraries", {
Expand All @@ -9,25 +6,89 @@ export const useLibrariesStore = defineStore("libraries", {
}),
actions: {
async getLibraries() {
let libraries: Array<{ id: string; name: string; created: Date }> = [];
const response = await axios()
.get("/api/libraries")
.catch((error) => {
errorToast(error);
});

if (response?.data) {
libraries = response.data.map((library: { id: string; name: string; created: string }) => {
return {
id: library.id,
name: library.name,
created: new Date(library.created),
};
});
// Get the libraries from the API
const response = await this.$axios.get("/api/libraries");

// Check if the response is valid
if (!response?.data) {
this.$toast.error("Could not get libraries");
return;
}

// Map the libraries to the correct format
this.libraries = response.data.map((library: { id: string; name: string; created: string }) => {
return {
id: library.id,
name: library.name,
created: new Date(library.created),
};
});
},
async saveLibraries(libraries: Array<{ id: string; name: string; selected: boolean }>) {
const formData = new FormData();
const newLibraries: string[] = [];

libraries.forEach((library) => {
if (library.selected) {
newLibraries.push(library.id);
}
});

formData.append("libraries", JSON.stringify(newLibraries));

const response = await this.$axios.post("/api/libraries", formData, { disableInfoToast: true }).catch(() => {
return;
});

if (!response?.data?.message) {
this.$toast.error("Could not save libraries");
return;
}

this.$toast.info("Successfully saved libraries");
},
async scanLibraries() {
// Get the libraries from the API
const libResponse = await this.$axios.get("/api/libraries");

// Check if the response is valid
if (!libResponse?.data) {
this.$toast.error("Could not get libraries");
return;
}

// Map the libraries to the correct format
const allLibraries = libResponse.data.map((library: { id: string; name: string; created: string }) => {
return {
id: library.id,
name: library.name,
created: new Date(library.created),
};
}) as Array<{ id: string; name: string; created: Date }>;

// Update the libraries in the store
this.libraries = allLibraries;

// Get the libraries from the media server
const scanResponse = await this.$axios.get("/api/scan-libraries");

// Check if the response is valid
if (!scanResponse?.data?.libraries) {
this.$toast.error("Could not get libraries");
return;
}

// Map the libraries to the correct format
const libraries: [string, string][] = Object.entries(scanResponse.data.libraries);
const newLibraries: Array<{ id: string; name: string; selected: boolean }> = [];

// Check if the library is selected
for (const [name, id] of libraries) {
const selected = allLibraries.find((library) => library.id === id) !== undefined;
newLibraries.push({ id: id, name: name, selected: selected });
}

this.libraries = libraries;
return libraries;
return newLibraries;
},
},
persist: true,
Expand Down

0 comments on commit 976615c

Please sign in to comment.