Skip to content

Commit

Permalink
feat: integration list page
Browse files Browse the repository at this point in the history
  • Loading branch information
lukicenturi committed Jul 2, 2024
1 parent 8919617 commit 7357fa4
Show file tree
Hide file tree
Showing 13 changed files with 737 additions and 75 deletions.
40 changes: 12 additions & 28 deletions components/account/CountrySelect.vue
Original file line number Diff line number Diff line change
@@ -1,55 +1,39 @@
<script setup lang="ts">
import { get } from '@vueuse/core';
import type { Country } from '~/composables/countries';
const props = withDefaults(
withDefaults(
defineProps<{
modelValue: string;
disabled?: boolean;
}>(),
{
disabled: false,
},
);
const emit = defineEmits<{
(e: 'update:modelValue', value: string): void;
}>();
const { modelValue } = toRefs(props);
const { t } = useI18n();
const { countries } = useCountries();
const country = computed({
get() {
return get(countries).find(({ code }) => code === get(modelValue)) || null;
},
set(item: Country | null) {
emit('update:modelValue', item ? item.code : '');
},
});
const modelValue = defineModel<string>({ required: true });
</script>

<template>
<RuiAutoComplete
id="country"
v-model="country"
v-model="modelValue"
variant="outlined"
color="primary"
autocomplete="country"
:data="countries"
:options="countries"
:disabled="disabled"
key-prop="code"
text-prop="name"
key-attr="code"
text-attr="name"
:label="t('auth.signup.address.form.country')"
:item-height="46"
:no-data-text="t('country_select.no_data')"
>
<template #no-data>
{{ t('country_select.no_data') }}
</template>
<template #default="{ item }">
{{ item.name }}
<template #item="{ item }">
<div class="py-2">
{{ item.name }}
</div>
</template>
</RuiAutoComplete>
</template>
47 changes: 21 additions & 26 deletions components/checkout/pay/CryptoAssetSelector.vue
Original file line number Diff line number Diff line change
@@ -1,19 +1,11 @@
<script lang="ts" setup>
import { storeToRefs } from 'pinia';
import { get } from '@vueuse/core';
import { get, set } from '@vueuse/core';
import { usePaymentCryptoStore } from '~/store/payments/crypto';
import { toTitleCase } from '~/utils/text';
import CryptoChainIcon from '~/components/checkout/pay/CryptoChainIcon.vue';
const props = defineProps<{
modelValue: string;
}>();
const emit = defineEmits<{
(e: 'update:modelValue', value: string): void;
}>();
const { modelValue } = toRefs(props);
const modelValue = defineModel<string>({ required: true });
const cryptoStore = usePaymentCryptoStore();
const { paymentAssets, paymentAssetsLoading } = storeToRefs(cryptoStore);
Expand All @@ -28,14 +20,14 @@ const blockchainItems: ComputedRef<Item[]> = computed(() => Object.keys(get(paym
label: toTitleCase(item),
})));
const selectedChain: Ref<Item | null> = ref(null);
const selectedChain: Ref<string> = ref('');
const tokenItems: ComputedRef<Item[]> = computed(() => {
const chain = get(selectedChain);
if (!chain)
return [];
const items = get(paymentAssets)[chain.id];
const items = get(paymentAssets)[chain];
if (!items)
return [];
Expand All @@ -60,7 +52,7 @@ const selectedToken = computed({
return get(tokenItems).find(({ id }) => id === get(modelValue)) || null;
},
set(item: Item | null) {
emit('update:modelValue', item ? item.id : '');
set(modelValue, item ? item.id : '');
},
});
Expand All @@ -69,7 +61,7 @@ const hint = computed(() => {
if (!chain)
return '';
const items = get(paymentAssets)[chain.id];
const items = get(paymentAssets)[chain];
if (!items)
return '';
Expand All @@ -88,13 +80,19 @@ const hint = computed(() => {
v-model="selectedChain"
variant="outlined"
color="primary"
:data="blockchainItems"
:options="blockchainItems"
:disabled="paymentAssetsLoading"
key-prop="id"
text-prop="label"
key-attr="id"
text-attr="label"
:label="t('home.plans.tiers.step_3.labels.network')"
>
<template #default="{ item }">
<template #item="{ item }">
<div class="flex items-center gap-3">
<CryptoChainIcon :chain="item.id" />
{{ item.label }}
</div>
</template>
<template #selection="{ item }">
<div class="flex items-center gap-3">
<CryptoChainIcon :chain="item.id" />
{{ item.label }}
Expand All @@ -106,16 +104,13 @@ const hint = computed(() => {
v-model="selectedToken"
variant="outlined"
color="primary"
:data="tokenItems"
:options="tokenItems"
:disabled="paymentAssetsLoading || !selectedChain"
key-prop="id"
text-prop="label"
key-attr="id"
text-attr="label"
:hint="hint"
return-object
:label="t('home.plans.tiers.step_3.labels.token')"
>
<template #default="{ item }">
{{ item.label }}
</template>
</RuiAutoComplete>
/>
</div>
</template>
Original file line number Diff line number Diff line change
Expand Up @@ -30,22 +30,16 @@ const menus: (Menu | MenuParent)[] = [
to: '/products',
highlightActive: true,
},
{
label: t('navigation_menu.integration.title'),
description: t('navigation_menu.integration.description'),
to: '/integrations',
},
{
label: t('navigation_menu.download'),
to: '/download',
highlightExactActive: true,
},
{
label: t('navigation_menu.jobs'),
to: '/jobs',
highlightActive: true,
},
{
label: t('navigation_menu.values'),
to: '/values',
highlightActive: true,
},
{
label: t('navigation_menu.resources'),
children: [
Expand Down Expand Up @@ -99,11 +93,11 @@ const { isMdAndDown } = useBreakpoint();
close-on-content-click
wrapper-class="w-full"
>
<template #activator="{ on }">
<template #activator="{ attrs }">
<RuiButton
variant="text"
class="w-full justify-start"
v-on="on"
v-bind="attrs"
>
<div class="flex items-center gap-2">
{{ menu.label }}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ const exchanges = [

<div class="flex-1">
<img
class="rounded-xl overflow-hidden"
class="overflow-hidden"
:alt="t('home.exchanges.title')"
src="/img/exchanges.png"
/>
Expand Down
165 changes: 165 additions & 0 deletions components/integration/IntegrationDetails.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
<script setup lang="ts">
import { get } from '@vueuse/core';
import LocalIntegrationData from '~/public/integrations/all.json';
enum TabCategory {
ALL = 'all',
BLOCKCHAINS = 'blockchains',
EXCHANGES = 'exchanges',
PROTOCOLS = 'protocols',
}
const search: Ref<string> = ref('');
const searchDebounced = refDebounced(search, 200);
const tab: Ref<TabCategory> = ref(TabCategory.ALL);
const { t } = useI18n();
const tabCategoriesLabel = computed(() => ({
[TabCategory.ALL]: t('integration.tabs.all'),
[TabCategory.BLOCKCHAINS]: t('integration.blockchains.title'),
[TabCategory.EXCHANGES]: t('integration.exchanges.title'),
[TabCategory.PROTOCOLS]: t('integration.protocols.title'),
}));
const data = computed(() => {
const searchVal = get(searchDebounced);
const filter = (data: { label: string; image: string }[]) => data.filter(item => item.label.toLocaleLowerCase().trim().includes(searchVal.toLocaleLowerCase().trim()));
const integrationData = LocalIntegrationData;
return {
[TabCategory.BLOCKCHAINS]: {
title: t('integration.blockchains.title'),
description: t('integration.blockchains.description'),
data: filter(integrationData.blockchains),
},
[TabCategory.EXCHANGES]: {
title: t('integration.exchanges.title'),
description: t('integration.exchanges.description'),
data: filter(integrationData.exchanges),
},
[TabCategory.PROTOCOLS]: {
title: t('integration.protocols.title'),
description: t('integration.protocols.description', { number: Math.floor(integrationData.protocols.length / 10) * 10 }),
data: filter(integrationData.protocols),
},
};
});
const isAllSelected: ComputedRef<boolean> = computed(() => get(tab) === 'all');
const total: ComputedRef<number> = computed(() => Object.values(get(data)).reduce((accumulator, item) => accumulator + item.data.length, 0));
const [DefineNotFound, ReuseNotFound] = createReusableTemplate<{
keyword: string;
}>();
const { isMdAndUp } = useBreakpoint();
</script>

<template>
<DefineNotFound
#default="{ keyword }"
class="text-rui-text-secondary"
>
{{ t('integration.no_data_found', { keyword }) }}
</DefineNotFound>
<div class="py-10 md:py-20">
<div class="container flex flex-col md:flex-row items-start gap-10 lg:gap-16">
<div class="md:sticky md:top-20 w-full md:w-[18rem]">
<RuiTextField
v-model="search"
variant="outlined"
:label="t('integration.search')"
prepend-icon="search-line"
color="primary"
clearable
/>
<RuiDivider class="mb-4" />
<ClientOnly>
<RuiTabs
v-model="tab"
color="primary"
:vertical="isMdAndUp"
grow
>
<RuiTab
v-for="(item, key) in tabCategoriesLabel"
:key="key"
:align="isMdAndUp ? 'start' : 'center'"
class="rounded cursor-pointer"
active-class="!bg-rui-primary [&:after]:hidden !text-white"
:tab-value="key"
>
{{ item }}
</RuiTab>
</RuiTabs>
</ClientOnly>
</div>
<TransitionGroup
tag="div"
enter-active-class="transition"
leave-active-class="transition h-0 overflow-hidden absolute"
enter-from-class="opacity-0 transform -translate-y-10"
leave-to-class="opacity-0"
class="flex-1 flex flex-col gap-10"
>
<ReuseNotFound
v-if="isAllSelected && total === 0"
:keyword="searchDebounced"
/>
<template
v-for="(datum, key) in data"
v-else
:key="key"
>
<div
v-if="(isAllSelected && datum.data.length > 0) || tab === key"
class="flex flex-col gap-10"
>
<div class="flex flex-col gap-4">
<div class="text-h4">
{{ datum.title }}
</div>
<div class="text-rui-text-secondary text-body-1">
{{ datum.description }}
</div>
</div>
<ReuseNotFound
v-if="datum.data.length === 0"
:keyword="searchDebounced"
/>
<TransitionGroup
v-else
tag="div"
move-class="transition-all"
enter-active-class="transition-all"
leave-active-class="transition-all absolute"
enter-from-class="opacity-0"
leave-to-class="opacity-0"
class="flex flex-wrap gap-4"
>
<template
v-for="item in datum.data"
:key="item.label"
>
<RuiTooltip :open-delay="200">
<template #activator>
<div class="w-14 h-14 border border-rui-grey-300 rounded p-2.5">
<img
class="w-full h-full"
:src="item.image"
:alt="item.label"
/>
</div>
</template>
{{ item.label }}
</RuiTooltip>
</template>
</TransitionGroup>
</div>
</template>
</TransitionGroup>
</div>
</div>
</template>
Loading

0 comments on commit 7357fa4

Please sign in to comment.