Biscuit
Biscuit
NNuxt
Created by Biscuit on 5/11/2024 in #❓・help
Using modelValue inside a nested div instead of the wrapping element.
<script setup lang="ts">
export type TextFieldType = "text" | "textarea" | "number" | "password";

export interface TextFieldProps {
name: string;
placeholder?: string;
label?: string;
required?: boolean;
modelValue: string | number;
outline?: boolean;
errorMessage?: string;
type?: TextFieldType;
disabled?: boolean;
noErrors?: boolean;
extraClasses?: string;
noPadding?: boolean;
}

const props = withDefaults(defineProps<TextFieldProps>(), {
label: undefined,
schema: undefined,
errorMessage: undefined,
type: "text",
extraClasses: undefined,
placeholder: undefined,
});

const emit = defineEmits(["update:modelValue"]);

const { modelValue } = toRefs(props);

const variant = computed(() =>
["text", "number", "password"].includes(props.type)
);

const inputClasses = computed(() => [
"overflow-hidden overflow-ellipsis font-normal text-base rounded-lg placeholder:text-gray-300 placeholder:text-base",
props.outline ? "outline outline-2" : "",
props.errorMessage
? "outline-error-600 color-error-600"
: "outline-gray-400 focus:outline-gray-700",
props.noPadding ? "p-0" : "px-3 py-2",
]);

const updateValue = (event) => {
emit("update:modelValue", event.target.value);
};
</script>
<script setup lang="ts">
export type TextFieldType = "text" | "textarea" | "number" | "password";

export interface TextFieldProps {
name: string;
placeholder?: string;
label?: string;
required?: boolean;
modelValue: string | number;
outline?: boolean;
errorMessage?: string;
type?: TextFieldType;
disabled?: boolean;
noErrors?: boolean;
extraClasses?: string;
noPadding?: boolean;
}

const props = withDefaults(defineProps<TextFieldProps>(), {
label: undefined,
schema: undefined,
errorMessage: undefined,
type: "text",
extraClasses: undefined,
placeholder: undefined,
});

const emit = defineEmits(["update:modelValue"]);

const { modelValue } = toRefs(props);

const variant = computed(() =>
["text", "number", "password"].includes(props.type)
);

const inputClasses = computed(() => [
"overflow-hidden overflow-ellipsis font-normal text-base rounded-lg placeholder:text-gray-300 placeholder:text-base",
props.outline ? "outline outline-2" : "",
props.errorMessage
? "outline-error-600 color-error-600"
: "outline-gray-400 focus:outline-gray-700",
props.noPadding ? "p-0" : "px-3 py-2",
]);

const updateValue = (event) => {
emit("update:modelValue", event.target.value);
};
</script>
9 replies
NNuxt
Created by Biscuit on 5/11/2024 in #❓・help
Using modelValue inside a nested div instead of the wrapping element.
this is the final version, f this defineModel bs
<template>
<div class="flex flex-col" :class="extraClasses">
<label v-if="label" class="flex mb-2 text-blue">
<p v-if="required" class="mr-1 text-xs text-error-600">*</p>
<p
class="text-sm font-semibold"
:class="[disabled ? 'text-gray-300' : 'text-gray-800']"
>
{{ label }}
</p>
</label>

<component
:is="variant ? 'input' : 'textarea'"
:id="name"
v-model="modelValue"
:name="name"
class="overflow-hidden overflow-ellipsis font-normal text-base rounded-lg placeholder:text-gray-300 placeholder:text-base"
:class="inputClasses"
:type="type"
:placeholder="placeholder ?? name"
:disabled="disabled"
:required="required"
v-bind="$attrs"
:aria-describedby="errorMessage ? `${name}-error` : null"
@input="updateValue"
/>
<span
v-if="!noErrors"
:id="`${name}-error`"
class="mt-1 text-error-500 h-3 text-[10px]"
>{{ errorMessage }}</span
>
</div>
</template>
<template>
<div class="flex flex-col" :class="extraClasses">
<label v-if="label" class="flex mb-2 text-blue">
<p v-if="required" class="mr-1 text-xs text-error-600">*</p>
<p
class="text-sm font-semibold"
:class="[disabled ? 'text-gray-300' : 'text-gray-800']"
>
{{ label }}
</p>
</label>

<component
:is="variant ? 'input' : 'textarea'"
:id="name"
v-model="modelValue"
:name="name"
class="overflow-hidden overflow-ellipsis font-normal text-base rounded-lg placeholder:text-gray-300 placeholder:text-base"
:class="inputClasses"
:type="type"
:placeholder="placeholder ?? name"
:disabled="disabled"
:required="required"
v-bind="$attrs"
:aria-describedby="errorMessage ? `${name}-error` : null"
@input="updateValue"
/>
<span
v-if="!noErrors"
:id="`${name}-error`"
class="mt-1 text-error-500 h-3 text-[10px]"
>{{ errorMessage }}</span
>
</div>
</template>
9 replies
NNuxt
Created by Biscuit on 5/11/2024 in #❓・help
Using modelValue inside a nested div instead of the wrapping element.
the fields are getting updated, but vee-validate claims the fields are empty when I can see they are not. I reckon this is not updating the property being passed down
9 replies
NNuxt
Created by Biscuit on 5/11/2024 in #❓・help
Using modelValue inside a nested div instead of the wrapping element.
usage
<div class="flex flex-col gap-2 mb-4">
<app-inputs-text-field
v-model:input="email"
label="Email"
name="email"
:error-message="errors.email"
v-bind="emailProps"
/>
<app-inputs-text-field
v-model:input="password"
label="Password"
name="password"
type="password"
:error-message="errors.password"
v-bind="passwordProps"
/>
</div>


import { useForm } from "vee-validate";

definePageMeta({
middleware: "control-access",
layout: "auth",
});
const { emailRules, passwordRules } = useFormRules();

const authStore = useAuthStore();

interface LoginForm {
email: string;
password: string;
}

const { controlledValues, handleSubmit, defineField, errors } =
useForm<LoginForm>({
validationSchema: { ...emailRules, password: passwordRules.password },
});

const [email, emailProps] = defineField("email");
const [password, passwordProps] = defineField("password");
<div class="flex flex-col gap-2 mb-4">
<app-inputs-text-field
v-model:input="email"
label="Email"
name="email"
:error-message="errors.email"
v-bind="emailProps"
/>
<app-inputs-text-field
v-model:input="password"
label="Password"
name="password"
type="password"
:error-message="errors.password"
v-bind="passwordProps"
/>
</div>


import { useForm } from "vee-validate";

definePageMeta({
middleware: "control-access",
layout: "auth",
});
const { emailRules, passwordRules } = useFormRules();

const authStore = useAuthStore();

interface LoginForm {
email: string;
password: string;
}

const { controlledValues, handleSubmit, defineField, errors } =
useForm<LoginForm>({
validationSchema: { ...emailRules, password: passwordRules.password },
});

const [email, emailProps] = defineField("email");
const [password, passwordProps] = defineField("password");
9 replies
NNuxt
Created by Biscuit on 5/11/2024 in #❓・help
Using modelValue inside a nested div instead of the wrapping element.
<script setup lang="ts">
export type TextFieldType = "text" | "textarea" | "number" | "password";

export interface TextFieldProps {
name: string;
placeholder?: string;
label?: string;
required?: boolean;
outline?: boolean;
errorMessage?: string;
type?: TextFieldType;
size?: Size;
disabled?: boolean;
noErrors?: boolean;
extraClasses?: string;
noPadding?: boolean;
}

const props = withDefaults(defineProps<TextFieldProps>(), {
label: undefined,
schema: undefined,
errorMessage: undefined,
type: "text",
size: "medium",
extraClasses: undefined,
placeholder: undefined,
});

const input = defineModel<string | number>("input");

const variant = computed(() =>
["text", "number", "password"].includes(props.type)
);

const inputClasses = computed(() => [
"overflow-hidden overflow-ellipsis font-normal text-base rounded-lg placeholder:text-gray-300 placeholder:text-base",
props.outline ? "outline outline-2" : "",
props.errorMessage
? "outline-error-600 color-error-600"
: "outline-gray-400 focus:outline-gray-700",
props.noPadding ? "p-0" : "px-3 py-2",
]);
</script>
<script setup lang="ts">
export type TextFieldType = "text" | "textarea" | "number" | "password";

export interface TextFieldProps {
name: string;
placeholder?: string;
label?: string;
required?: boolean;
outline?: boolean;
errorMessage?: string;
type?: TextFieldType;
size?: Size;
disabled?: boolean;
noErrors?: boolean;
extraClasses?: string;
noPadding?: boolean;
}

const props = withDefaults(defineProps<TextFieldProps>(), {
label: undefined,
schema: undefined,
errorMessage: undefined,
type: "text",
size: "medium",
extraClasses: undefined,
placeholder: undefined,
});

const input = defineModel<string | number>("input");

const variant = computed(() =>
["text", "number", "password"].includes(props.type)
);

const inputClasses = computed(() => [
"overflow-hidden overflow-ellipsis font-normal text-base rounded-lg placeholder:text-gray-300 placeholder:text-base",
props.outline ? "outline outline-2" : "",
props.errorMessage
? "outline-error-600 color-error-600"
: "outline-gray-400 focus:outline-gray-700",
props.noPadding ? "p-0" : "px-3 py-2",
]);
</script>
9 replies
NNuxt
Created by Biscuit on 5/11/2024 in #❓・help
Using modelValue inside a nested div instead of the wrapping element.
I'm refactoring the code rn and it isn't quite working anymore.
<template>
<div class="flex flex-col" :class="extraClasses">
<label v-if="label" class="flex mb-2 text-blue">
<p v-if="required" class="mr-1 text-xs text-error-600">*</p>
<p
class="text-sm font-semibold"
:class="[disabled ? 'text-gray-300' : 'text-gray-800']"
>
{{ label }}
</p>
</label>

<component
:is="variant ? 'input' : 'textarea'"
:id="name"
v-model="input"
:name="name"
class="overflow-hidden overflow-ellipsis font-normal text-base rounded-lg placeholder:text-gray-300 placeholder:text-base"
:class="inputClasses"
:type="type"
:placeholder="placeholder ?? name"
:disabled="disabled"
:required="required"
v-bind="$attrs"
:aria-describedby="errorMessage ? `${name}-error` : null"
/>
<span
v-if="!noErrors"
:id="`${name}-error`"
class="mt-1 text-error-500 h-3 text-[10px]"
>{{ errorMessage }}</span
>
</div>
</template>
<template>
<div class="flex flex-col" :class="extraClasses">
<label v-if="label" class="flex mb-2 text-blue">
<p v-if="required" class="mr-1 text-xs text-error-600">*</p>
<p
class="text-sm font-semibold"
:class="[disabled ? 'text-gray-300' : 'text-gray-800']"
>
{{ label }}
</p>
</label>

<component
:is="variant ? 'input' : 'textarea'"
:id="name"
v-model="input"
:name="name"
class="overflow-hidden overflow-ellipsis font-normal text-base rounded-lg placeholder:text-gray-300 placeholder:text-base"
:class="inputClasses"
:type="type"
:placeholder="placeholder ?? name"
:disabled="disabled"
:required="required"
v-bind="$attrs"
:aria-describedby="errorMessage ? `${name}-error` : null"
/>
<span
v-if="!noErrors"
:id="`${name}-error`"
class="mt-1 text-error-500 h-3 text-[10px]"
>{{ errorMessage }}</span
>
</div>
</template>
9 replies
NNuxt
Created by Biscuit on 5/11/2024 in #❓・help
Using modelValue inside a nested div instead of the wrapping element.
<script setup lang="ts">
export type TextFieldType = "text" | "textarea" | "number" | "password";

export interface TextFieldProps {
modelValue: string | number;
name: string;
placeholder?: string;
label?: string;
required?: boolean;
outline?: boolean;
errorMessage?: string;
type?: TextFieldType;
size?: Size;
disabled?: boolean;
noErrors?: boolean;
extraClasses?: string;
}

const props = withDefaults(defineProps<TextFieldProps>(), {
// info: undefined,
label: undefined,
modelValue: undefined,
schema: undefined,
errorMessage: undefined,
type: "text",
size: "medium",
extraClasses: undefined,
placeholder: undefined,
});

const { modelValue } = toRefs(props);

const variant = computed(() =>
["text", "number", "password"].includes(props.type)
);
<script setup lang="ts">
export type TextFieldType = "text" | "textarea" | "number" | "password";

export interface TextFieldProps {
modelValue: string | number;
name: string;
placeholder?: string;
label?: string;
required?: boolean;
outline?: boolean;
errorMessage?: string;
type?: TextFieldType;
size?: Size;
disabled?: boolean;
noErrors?: boolean;
extraClasses?: string;
}

const props = withDefaults(defineProps<TextFieldProps>(), {
// info: undefined,
label: undefined,
modelValue: undefined,
schema: undefined,
errorMessage: undefined,
type: "text",
size: "medium",
extraClasses: undefined,
placeholder: undefined,
});

const { modelValue } = toRefs(props);

const variant = computed(() =>
["text", "number", "password"].includes(props.type)
);
9 replies
NNuxt
Created by Biscuit on 5/1/2024 in #❓・help
A composable that requires access to the Nuxt instance was called outside of a plugin,
login page
<template>
<div class="grid xs:grid-cols-2 h-full">
<div class="hidden xs:flex w-full bg-primary-800" />
<custom-card
title="login"
:img="{ src: 'logo.png', height: 100, width: 100, alt: 'gighub' }"
>
<template #content>
c
</template>
</custom-card>
</div>
</template>

<script setup lang="ts">
definePageMeta({
middleware: "control-access",
layout: "auth",
});
<template>
<div class="grid xs:grid-cols-2 h-full">
<div class="hidden xs:flex w-full bg-primary-800" />
<custom-card
title="login"
:img="{ src: 'logo.png', height: 100, width: 100, alt: 'gighub' }"
>
<template #content>
c
</template>
</custom-card>
</div>
</template>

<script setup lang="ts">
definePageMeta({
middleware: "control-access",
layout: "auth",
});
3 replies
NNuxt
Created by Biscuit on 5/1/2024 in #❓・help
A composable that requires access to the Nuxt instance was called outside of a plugin,
composable
export const useAccessToken = (baseUrl: string) => {
const authHeaders = () => {
return {
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${useCookie("token").value}`,
} as HeadersInit,
};
};

const setAccessToken = (token: string | undefined | null) => {
if (!token) return;
useCookie("token").value = token;
};

const refreshAccessToken = async () => {
try {
const response = await fetch(`${baseUrl}/refresh-token`, {
headers: {
"Content-Type": "application/json",
},
credentials: "include",
});

// Check if the request was successful
if (!response.ok) {
throw new Error("Refresh token failed");
}

// If successful, you can process the response here
const data = await response.json();
console.log({ refresh: data });
} catch (err) {
console.log({ err });
useCookie("token").value = null;
}
};

const verifyAccessToken = async () => {
try {
const response = await fetch(`${baseUrl}/me`, {
headers: authHeaders().headers,
});

// Check if the request was successful
if (!response.ok) {
throw new Error("Verify token failed");
}

// If successful, you can process the response here
const data = await response.json();
console.log({ refresh: data });
} catch (err) {
console.log({ err });
await refreshAccessToken();
}
};

return { authHeaders, setAccessToken, refreshAccessToken, verifyAccessToken };
};
export const useAccessToken = (baseUrl: string) => {
const authHeaders = () => {
return {
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${useCookie("token").value}`,
} as HeadersInit,
};
};

const setAccessToken = (token: string | undefined | null) => {
if (!token) return;
useCookie("token").value = token;
};

const refreshAccessToken = async () => {
try {
const response = await fetch(`${baseUrl}/refresh-token`, {
headers: {
"Content-Type": "application/json",
},
credentials: "include",
});

// Check if the request was successful
if (!response.ok) {
throw new Error("Refresh token failed");
}

// If successful, you can process the response here
const data = await response.json();
console.log({ refresh: data });
} catch (err) {
console.log({ err });
useCookie("token").value = null;
}
};

const verifyAccessToken = async () => {
try {
const response = await fetch(`${baseUrl}/me`, {
headers: authHeaders().headers,
});

// Check if the request was successful
if (!response.ok) {
throw new Error("Verify token failed");
}

// If successful, you can process the response here
const data = await response.json();
console.log({ refresh: data });
} catch (err) {
console.log({ err });
await refreshAccessToken();
}
};

return { authHeaders, setAccessToken, refreshAccessToken, verifyAccessToken };
};
3 replies