Skip to content

Commit

Permalink
feat: revamp user home page
Browse files Browse the repository at this point in the history
Co-authored-by: Joseph Ojoko <entewshi@gmail.com>
  • Loading branch information
2 people authored and kelsos committed Sep 27, 2023
1 parent 9cf4bea commit f8ad2d3
Show file tree
Hide file tree
Showing 58 changed files with 1,471 additions and 1,425 deletions.
293 changes: 27 additions & 266 deletions components/account/CountrySelect.vue
Original file line number Diff line number Diff line change
@@ -1,291 +1,52 @@
<script setup lang="ts">
import { debouncedWatch, get, set } from '@vueuse/core';
import { get } from '@vueuse/core';
import { type Country } from '~/composables/countries';
import { type BaseErrorObject } from '~/types/common';
const props = withDefaults(
defineProps<{
modelValue: string;
id: string;
label: string;
countries?: Country[];
hint?: string;
disabled?: boolean;
errorMessages?: BaseErrorObject[];
autocomplete?: string;
}>(),
{
countries: () => [],
hint: '',
disabled: false,
errorMessages: () => [],
autocomplete: undefined,
},
);
const emit = defineEmits<{
(e: 'update:modelValue', value: string): void;
(e: 'blur'): void;
(e: 'selected', country: Country | null): void;
}>();
const { countries, modelValue: value, disabled } = toRefs(props);
const selected = ref<Country | null>(null);
const optionsShown = ref(false);
const searchFilter = ref('');
const { modelValue } = toRefs(props);
const filteredOptions = computed(() => {
const filtered: Country[] = [];
const filter = get(searchFilter);
const regOption = new RegExp(filter, 'ig');
for (const option of get(countries)) {
if (filter.length === 0 || option.name.match(regOption)) {
filtered.push(option);
}
}
return filtered;
});
const selectOption = (option: Country) => {
set(selected, option);
set(optionsShown, false);
set(searchFilter, option.name);
emit('update:modelValue', option.code);
};
const showOptions = () => {
if (!get(disabled)) {
set(searchFilter, '');
set(optionsShown, true);
}
};
const { t } = useI18n();
const exit = () => {
const filtered = get(filteredOptions);
if (filtered.length > 0 && get(searchFilter)) {
selectOption(filtered[0]);
} else {
const selection = get(selected);
if (selection) {
selectOption(selection);
} else {
set(searchFilter, '');
}
}
set(optionsShown, false);
emit('blur');
};
const { countries } = useCountries();
const keyMonitor = (event: KeyboardEvent) => {
const filtered = get(filteredOptions);
if (event.key === 'Enter' && filtered[0]) {
selectOption(filtered[0]);
}
};
const getFlagEmoji = (code: string) => {
const codePoints = code
.toUpperCase()
.split('')
.map((char) => 127397 + char.charCodeAt(0));
return String.fromCodePoint(...codePoints);
};
const isEmpty = computed(() => {
const filter = get(searchFilter);
const selection = get(selected);
return !filter || selection?.name !== filter;
});
watch(selected, (newValue, oldValue) => {
if (newValue && newValue !== oldValue) {
emit('update:modelValue', newValue?.code ?? '');
}
});
onMounted(() => setCountryUsingCode(get(value)));
function setCountryUsingCode(value: string) {
const cnt = get(countries);
const country = cnt.find(({ code }) => code === value);
if (country && country !== get(selected)) {
selectOption(country);
}
}
debouncedWatch(
value,
(value) => {
if (!value) {
set(selected, null);
} else {
setCountryUsingCode(value);
}
const country = computed({
get() {
return get(countries).find(({ code }) => code === get(modelValue)) || null;
},
{ debounce: 800 },
);
emit('selected', get(selected));
const css = useCssModule();
set(item: Country | null) {
emit('update:modelValue', item ? item.code : '');
},
});
</script>

<template>
<div :class="css.wrapper">
<div>
<div :class="css.inputContainer">
<div v-if="countries" :class="css.dropdown">
<span
v-if="selected && searchFilter === selected.name"
:class="css.flag"
>
{{ getFlagEmoji(selected.code) }}</span
>
<input
:id="id"
v-model="searchFilter"
:name="id"
autocomplete="off"
:class="{
[css.input]: true,
[css.empty]: isEmpty,
}"
:disabled="disabled"
@focus="showOptions()"
@blur="exit()"
@keyup="keyMonitor($event)"
/>
<label
v-if="label"
:class="{
[css.label]: true,
[css.select]: true,
}"
:for="id"
>
{{ label }}
</label>

<div v-show="optionsShown" :class="css.content">
<div
v-for="(option, index) in filteredOptions"
:key="index"
:class="css.item"
@mousedown="selectOption(option)"
>
{{ getFlagEmoji(option.code) }}
{{ option.name || '-' }}
</div>
</div>
</div>
</div>
</div>

<span
v-if="errorMessages && errorMessages.length > 0"
:id="`${id}-error`"
:class="css.error"
>
{{ errorMessages[0].$message }}
</span>

<span v-else :class="css.caption">
<slot name="hint">{{ hint }}</slot>
</span>
<slot />
</div>
<RuiAutoComplete
id="country"
v-model="country"
variant="outlined"
color="primary"
autocomplete="country"
:data="countries"
:disabled="disabled"
key-prop="code"
text-prop="name"
:label="t('auth.signup.address.form.country')"
>
<template #no-data>
{{ t('country_select.no_data') }}
</template>
</RuiAutoComplete>
</template>

<style lang="scss" module>
.wrapper {
@apply flex flex-col pt-2.5;
}
.inputContainer {
@apply relative w-full;
}
.input {
@apply block border-rui-grey-300 box-border border-solid focus:outline-none focus:border-rui-primary pr-2 px-3 appearance-none w-full bg-white;
margin-top: 8px;
border-width: 1px;
border-radius: 8px;
height: 56px;
& ~ label {
@apply top-4;
transform-origin: 0 0;
left: 26px;
}
&:focus-within ~ label {
@apply transform scale-75 -translate-y-4 text-rui-primary-darker duration-300;
}
&:not(.empty) {
@apply pl-9;
}
&:not(.empty):not(:focus-within) ~ label {
@apply transform scale-75 -translate-y-4 text-rui-text-secondary duration-300;
}
&:disabled {
@apply bg-gray-200;
}
}
.label {
@apply absolute top-4 text-rui-text-secondary text-sm;
}
.dropdown {
@apply relative block m-auto;
.dropdown:hover .content {
@apply block;
}
}
.content {
@apply absolute bg-white max-h-60 border border-solid border-rui-grey-200 shadow-md overflow-auto z-10;
min-width: 280px;
max-width: 280px;
}
.item {
@apply text-base text-rui-grey-800 p-2 block cursor-pointer;
text-decoration: none;
&:hover {
background-color: #e7ecf5;
}
}
%text-style {
@apply text-xs font-sans;
color: #808080;
margin-top: 8px;
}
.caption {
@extend %text-style;
}
.error {
color: #e53935;
@extend %text-style;
}
.empty {
@apply pl-3;
}
.flag {
@apply absolute top-4 left-3;
}
</style>
2 changes: 1 addition & 1 deletion components/account/activation/AccountActivate.vue
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ const { t } = useI18n();
<span>
{{ t('auth.activation.success.message') }}
</span>
<ButtonLink to="/home" inline color="primary">
<ButtonLink to="/home/subscription" inline color="primary">
{{ t('common.here') }}
</ButtonLink>
</div>
Expand Down
2 changes: 1 addition & 1 deletion components/account/activation/PendingActivation.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ const { t } = useI18n();
<div
class="container h-full flex flex-col pt-10 md:pt-0 md:flex-1 md:items-center md:justify-center"
>
<div class="max-w-[30.6rem] max-w-full space-y-3">
<div class="w-[30.6rem] max-w-full space-y-3">
<div class="text-h4">{{ t('auth.pending_activation.title') }}</div>
<div class="text-body-1 text-rui-text-secondary">
{{ t('auth.pending_activation.message') }}
Expand Down
23 changes: 14 additions & 9 deletions components/account/delete/AccountDeletedPage.vue
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
<script setup lang="ts">
const { t } = useI18n();
</script>

<template>
<PageContainer>
<template #title> Account Delete </template>
<PageContent>
<UserActionMessage>
<template #header>Your account was deleted</template>
<p>Your rotki premium account has been successfully deleted.</p>
</UserActionMessage>
</PageContent>
</PageContainer>
<div
class="container h-full flex flex-col pt-10 md:pt-0 md:flex-1 md:items-center md:justify-center"
>
<div class="max-w-[380px] max-w-full space-y-3">
<div class="text-h4">{{ t('account.account_deleted.title') }}</div>
<div class="text-body-1 text-rui-text-secondary">
{{ t('account.account_deleted.message') }}
</div>
</div>
</div>
</template>
Loading

0 comments on commit f8ad2d3

Please sign in to comment.