Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Global UX improvements and fixes #302

Merged
merged 8 commits into from
Aug 17, 2024
2 changes: 1 addition & 1 deletion src/components/fields/form-wrapper.vue
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ defineProps<{
display: grid;
grid-gap: 8px;

animation: slide-appear 1s ease-out 300ms both;
// animation: slide-appear 1s ease-out 2000ms both;
}
.form-wrapper__error {
text-align: center;
Expand Down
19 changes: 19 additions & 0 deletions src/components/lib/ui/tooltip/Tooltip.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<script setup lang="ts">
import {
TooltipRoot,
type TooltipRootEmits,
type TooltipRootProps,
useForwardPropsEmits,
} from "radix-vue";

const props = defineProps<TooltipRootProps>();
const emits = defineEmits<TooltipRootEmits>();

const forwarded = useForwardPropsEmits(props, emits);
</script>

<template>
<TooltipRoot v-bind="forwarded">
<slot />
</TooltipRoot>
</template>
50 changes: 50 additions & 0 deletions src/components/lib/ui/tooltip/TooltipContent.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<script setup lang="ts">
import { type HTMLAttributes, computed } from "vue";
import {
TooltipContent,
type TooltipContentEmits,
type TooltipContentProps,
TooltipPortal,
useForwardPropsEmits,
} from "radix-vue";
import { cn } from "@/lib/utils";

defineOptions({
inheritAttrs: false,
});

const props = withDefaults(
defineProps<TooltipContentProps & { class?: HTMLAttributes["class"] }>(),
{
sideOffset: 4,
class: "",
},
);

const emits = defineEmits<TooltipContentEmits>();

const delegatedProps = computed(() => {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { class: _, ...delegated } = props;

return delegated;
});

const forwarded = useForwardPropsEmits(delegatedProps, emits);
</script>

<template>
<TooltipPortal>
<TooltipContent
v-bind="{ ...forwarded, ...$attrs }"
:class="
cn(
'z-50 overflow-hidden rounded-md border bg-popover px-3 py-1.5 text-sm text-popover-foreground shadow-md animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
props.class,
)
"
>
<slot />
</TooltipContent>
</TooltipPortal>
</template>
11 changes: 11 additions & 0 deletions src/components/lib/ui/tooltip/TooltipProvider.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<script setup lang="ts">
import { TooltipProvider, type TooltipProviderProps } from "radix-vue";

const props = defineProps<TooltipProviderProps>();
</script>

<template>
<TooltipProvider v-bind="props">
<slot />
</TooltipProvider>
</template>
11 changes: 11 additions & 0 deletions src/components/lib/ui/tooltip/TooltipTrigger.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<script setup lang="ts">
import { TooltipTrigger, type TooltipTriggerProps } from "radix-vue";

const props = defineProps<TooltipTriggerProps>();
</script>

<template>
<TooltipTrigger v-bind="props">
<slot />
</TooltipTrigger>
</template>
4 changes: 4 additions & 0 deletions src/components/lib/ui/tooltip/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export { default as Tooltip } from "./Tooltip.vue";
export { default as TooltipContent } from "./TooltipContent.vue";
export { default as TooltipTrigger } from "./TooltipTrigger.vue";
export { default as TooltipProvider } from "./TooltipProvider.vue";
199 changes: 83 additions & 116 deletions src/components/modals/monobank-set-token.vue
Original file line number Diff line number Diff line change
@@ -1,170 +1,137 @@
<template>
<div class="monobank-set-token" data-cy="monobank-set-token-modal">
<ui-button
<Card
class="py-12 px-8 w-full max-w-[600px] relative rounded-xl"
data-cy="monobank-set-token-modal"
>
<Button
type="button"
class="button-style-reset monobank-set-token__close"
class="absolute top-3 right-3"
theme="light-dark"
is-icon
@click="$emit(MODAL_EVENTS.closeModal)"
>
X
</ui-button>
</Button>

<p class="monobank-set-token__descr">
<p class="my-5">
Please visit
<a href="https://api.monobank.ua/">https://api.monobank.ua/</a>
<a href="https://api.monobank.ua/" class="text-primary"
>https://api.monobank.ua/</a
>
and follow all the instructions. Paste the API token from Monobank in the
field below
</p>
<div class="monobank-set-token__row">
<div class="my-5">
<input-field
v-model="form.token"
name="token"
label="API Token"
:error-message="getFieldErrorMessage('form.token')"
/>
</div>
<div class="monobank-set-token__actions">
<ui-button
class="monobank-set-token__action monobank-set-token__action--submit"
type="submit"
:disabled="isLoading"
@click="submit"
>
<div class="flex justify-center">
<Button type="submit" :disabled="isLoading" @click="submit">
<template v-if="isUpdate"> Update token </template>
<template v-else> Pair account </template>
</ui-button>
</Button>
</div>
</div>
</Card>
</template>

<script lang="ts">
import { defineComponent, reactive, ref } from "vue";
<script setup lang="ts">
import { reactive, ref } from "vue";
import { API_ERROR_CODES } from "shared-types";
import { useBanksMonobankStore } from "@/stores";
import { useBanksMonobankStore, useCurrenciesStore } from "@/stores";
import { MONOBANK_API_TOKEN_LENGTH } from "@/common/const";
import { ApiErrorResponseError } from "@/js/errors";
import { useFormValidation } from "@/composable";
import { required, minLength } from "@/js/helpers/validators";
import InputField from "@/components/fields/input-field.vue";
import Button from "@/components/common/ui-button.vue";
import { EVENTS as MODAL_EVENTS } from "@/components/modal-center/ui-modal.vue";
import { Card } from "@/components/lib/ui/card";

import {
useNotificationCenter,
NotificationType,
} from "@/components/notification-center";

export default defineComponent({
defineOptions({
name: "monobank-set-token",
components: {
"input-field": InputField,
"ui-button": Button,
},
props: {
isUpdate: { type: Boolean, default: false },
});

const props = withDefaults(
defineProps<{
isUpdate?: boolean;
}>(),
{
isUpdate: false,
},
emits: [MODAL_EVENTS.closeModal],
setup(props, { emit }) {
const monobankStore = useBanksMonobankStore();
const { addNotification } = useNotificationCenter();
);

const isLoading = ref(false);
const form = reactive({ token: "" });
const { isFormValid, getFieldErrorMessage } = useFormValidation(
{ form },
{
form: {
token: {
required,
apiToken: minLength(MONOBANK_API_TOKEN_LENGTH),
},
},
},
undefined,
{
customValidationMessages: {
apiToken: `Monobank API token should be ${MONOBANK_API_TOKEN_LENGTH} characters length`,
},
const emit = defineEmits([MODAL_EVENTS.closeModal]);
const monobankStore = useBanksMonobankStore();
const currenciesStore = useCurrenciesStore();
const { addNotification } = useNotificationCenter();

const isLoading = ref(false);
const form = reactive({ token: "" });
const { isFormValid, getFieldErrorMessage } = useFormValidation(
{ form },
{
form: {
token: {
required,
apiToken: minLength(MONOBANK_API_TOKEN_LENGTH),
},
);
},
},
undefined,
{
customValidationMessages: {
apiToken: `Monobank API token should be ${MONOBANK_API_TOKEN_LENGTH} characters length`,
},
},
);

const submit = async () => {
try {
if (!isFormValid()) return;
const submit = async () => {
try {
if (!isFormValid()) return;

isLoading.value = true;
isLoading.value = true;

if (props.isUpdate) {
await monobankStore.updateUser({ token: form.token });
} else {
await monobankStore.pairAccount({ token: form.token });
}
if (props.isUpdate) {
await monobankStore.updateUser({ token: form.token });
} else {
await monobankStore.pairAccount({ token: form.token });
}

emit(MODAL_EVENTS.closeModal);
await currenciesStore.loadCurrencies();

addNotification({
text: "Paired",
type: NotificationType.success,
});
} catch (e) {
if (e instanceof ApiErrorResponseError) {
if (e.data.code === API_ERROR_CODES.monobankUserAlreadyConnected) {
addNotification({
text: "Account already connected",
type: NotificationType.error,
});

return;
}
}
emit(MODAL_EVENTS.closeModal);

addNotification({
text: "Paired",
type: NotificationType.success,
});
} catch (e) {
if (e instanceof ApiErrorResponseError) {
if (e.data.code === API_ERROR_CODES.monobankUserAlreadyConnected) {
addNotification({
text: "Unexpected error",
text: "Account already connected",
type: NotificationType.error,
});
} finally {
isLoading.value = false;
}
};

return {
MODAL_EVENTS,
form,
isLoading,
submit,
getFieldErrorMessage,
};
},
});
</script>

<style lang="scss" scoped>
.monobank-set-token {
background-color: var(--app-bg-box);
padding: 48px 32px;
width: 100%;
max-width: 600px;
position: relative;
border-radius: 12px;
}
.monobank-set-token__close {
position: absolute;
top: 12px;
right: 12px;
}
.monobank-set-token__row {
margin-bottom: 20px;
}
.monobank-set-token__actions {
display: flex;
justify-content: center;
}
.monobank-set-token__descr {
margin: 20px 0;
return;
}
}

a {
color: var(--app-primary);
addNotification({
text: "Unexpected error",
type: NotificationType.error,
});
} finally {
isLoading.value = false;
}
}
</style>
};
</script>
Loading
Loading