Skip to content

Commit

Permalink
feat(frontend): enhanced ux (#92)
Browse files Browse the repository at this point in the history
* feat(frontend): enhanced ux

* fix(frontend): text case on contest problem list
  • Loading branch information
thezzisu authored Apr 6, 2024
1 parent 327bf0b commit 5edc50b
Show file tree
Hide file tree
Showing 13 changed files with 65 additions and 34 deletions.
2 changes: 2 additions & 0 deletions .yarn/versions/6e05125d.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
declined:
- "@aoi-js/frontend"
8 changes: 5 additions & 3 deletions apps/frontend/src/components/aoi/AoiBar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<template #prepend>
<VIcon size="48"><AoiLogo /></VIcon>
</template>
<div class="text-none u-pl-4 u-font-mono u-text-3xl">
<div v-if="mdAndUp" class="text-none u-pl-4 u-font-mono u-text-3xl">
{{ appName }}
</div>
</VBtn>
Expand All @@ -16,10 +16,10 @@

<VSpacer></VSpacer>

<SearchBox v-if="appState.orgId" :org-id="appState.orgId" />
<SearchBox v-if="mdAndUp && appState.orgId" />
<VToolbarItems v-if="appState.loggedIn">
<AoiBarAddMenu />
<AoiBarUserMenu />
<AoiBarUserMenu :dense="!mdAndUp" />
</VToolbarItems>
<VToolbarItems v-else>
<VBtn color="blue-darken-1" to="/auth/login" exact>
Expand All @@ -34,6 +34,7 @@

<script setup lang="ts">
import { useI18n } from 'vue-i18n'
import { useDisplay } from 'vuetify'
import SearchBox from '../homepage/SearchBox.vue'
import TimeLabel from '../homepage/TimeLabel.vue'
Expand All @@ -47,4 +48,5 @@ import { appName, showCountdown } from '@/utils/flags'
const { t } = useI18n()
const appState = useAppState()
const { mdAndUp } = useDisplay()
</script>
6 changes: 5 additions & 1 deletion apps/frontend/src/components/aoi/AoiBarUserMenu.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<AoiGravatar :email="userInfo.state.value?.profile.email ?? ''" />
</VAvatar>
</template>
{{ userInfo.state.value?.profile.name }}
{{ dense ? '' : userInfo.state.value?.profile.name }}
</VBtn>
</template>
<VList>
Expand All @@ -22,6 +22,10 @@ import AoiGravatar from './AoiGravatar.vue'
import { useAppState } from '@/stores/app'
import { useAppUserMenu } from '@/utils/menus'
defineProps<{
dense?: boolean
}>()
const app = useAppState()
const userInfo = app.user
Expand Down
3 changes: 2 additions & 1 deletion apps/frontend/src/components/contest/ContestList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -61,13 +61,14 @@
import { computed, watch } from 'vue'
import { useI18n } from 'vue-i18n'
import ContestStageChip from './ContestStageChip.vue'
import type { IContestDTO } from './types'
import AccessLevelBadge from '@/components/utils/AccessLevelBadge.vue'
import ContestStageChip from '@/components/utils/ContestStageChip.vue'
import { usePagination } from '@/utils/pagination'
import { denseDateString } from '@/utils/time'
import { withTitle } from '@/utils/title'
const props = defineProps<{
orgId: string
search?: string
Expand Down
20 changes: 17 additions & 3 deletions apps/frontend/src/components/contest/ContestTabs.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
<template>
<VTabs align-tabs="center" class="u-flex-1">
<VTabs align-tabs="title" class="u-min-w-64">
<VTab class="text-none" selected-class="" color="" :to="rel('')" :text="contest?.title ?? ''" />
<VSlideYReverseTransition>
<div class="u-self-center u-flex u-gap-2" v-if="contest && !headerVisible">
<VChip color="success" v-if="registered" :text="t('msg.registered')" />
<VChip color="info" v-else :text="t('msg.not-registered')" />
<ContestStageChip :stages="contest.stages" :now="+now" />
</div>
</VSlideYReverseTransition>
</VTabs>
<VSpacer class="u-min-w-4" />
<VTabs align-tabs="end">
<VTab prepend-icon="mdi-book-outline" exact :to="rel('')" :text="t('tabs.description')" />
<VTab prepend-icon="mdi-attachment" :to="rel('attachment')" :text="t('tabs.attachments')" />
<VTab
Expand Down Expand Up @@ -36,20 +47,23 @@
</template>

<script setup lang="ts">
import { useNow } from '@vueuse/core'
import { useI18n } from 'vue-i18n'
import ContestStageChip from './ContestStageChip.vue'
import type { IContestDTO } from '@/components/contest/types'
import { useAppState } from '@/stores/app'
const { t } = useI18n()
const app = useAppState()
const props = defineProps<{
orgId: string
contestId: string
showAdminTab: boolean
registered: boolean
contest: IContestDTO
headerVisible: boolean
}>()
const rel = (to: string) => `/org/${props.orgId}/contest/${props.contestId}/${to}`
const now = useNow()
</script>
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@
import { useAsyncState } from '@vueuse/core'
import { useI18n } from 'vue-i18n'
import ContestStageChip from '@/components/contest/ContestStageChip.vue'
import AsyncState from '@/components/utils/AsyncState.vue'
import ContestStageChip from '@/components/utils/ContestStageChip.vue'
import { http } from '@/utils/http'
const { t } = useI18n()
Expand Down
25 changes: 11 additions & 14 deletions apps/frontend/src/components/homepage/SearchBox.vue
Original file line number Diff line number Diff line change
@@ -1,37 +1,34 @@
<template>
<VTextField
v-if="route.params.orgId"
v-model="search"
hide-details
class="u-max-w-64"
density="compact"
:label="t('term.search')"
append-icon="mdi-magnify"
append-inner-icon="mdi-magnify"
clearable
@click:append="onSearch"
@click:append-inner="onSearch"
@keyup.enter="onSearch"
/>
</template>

<script setup lang="ts">
import { ref } from 'vue'
import { useI18n } from 'vue-i18n'
import { useRouter } from 'vue-router'
const props = defineProps<{
orgId: string
}>()
import { useRoute, useRouter } from 'vue-router'
const router = useRouter()
const route = useRoute()
const { t } = useI18n()
const search = ref('')
function onSearch() {
router.push({
path: `/org/${props.orgId}/problem/search`,
query: { search: search.value }
})
if (search.value && route.params.orgId) {
router.push({
path: `/org/${route.params.orgId}/problem/search`,
query: { search: search.value }
})
}
}
// TODO: check if satisfy the requirement of !CLIENT!
</script>
2 changes: 2 additions & 0 deletions apps/frontend/src/locales/en.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,8 @@ msg:
Please upgrade your web browser to the latest version,
or switch to a modern web browser.
recommended-browsers: Recommended web browsers
registered: Registered
not-registered: Not Registered

tabs:
description: Description
Expand Down
2 changes: 2 additions & 0 deletions apps/frontend/src/locales/zh-Hans.yml
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,8 @@ msg:
{issues}
建议您升级浏览器到最新版本,或者切换为另一种现代浏览器。
recommended-browsers: 推荐的浏览器
registered: 已报名
not-registered: 未报名

tabs:
description: 描述
Expand Down
22 changes: 14 additions & 8 deletions apps/frontend/src/pages/org/[orgId]/contest/[contestId].vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<template v-slot="{ value }">
<VRow>
<VCol>
<VCard variant="text">
<VCard variant="text" v-intersect="{ handler: onIntersect, options: { rootMargin } }">
<VCardTitle class="d-flex justify-space-between">
<div>
<p class="text-h4">{{ value.title }}</p>
Expand Down Expand Up @@ -40,8 +40,8 @@

<script setup lang="ts">
import { useAsyncState } from '@vueuse/core'
import { computed, toRef } from 'vue'
import { useI18n } from 'vue-i18n'
import { computed, ref, toRef } from 'vue'
import { useLayout } from 'vuetify'
import ContestProgressBar from '@/components/contest/ContestProgressBar.vue'
import ContestTabs from '@/components/contest/ContestTabs.vue'
Expand All @@ -52,21 +52,19 @@ import RegisterBtn from '@/components/utils/RegisterBtn.vue'
import { useContest } from '@/utils/contest/inject'
import { useContestProblemList } from '@/utils/contest/problem/inject'
import { http } from '@/utils/http'
import { withNavExtension, withTitle } from '@/utils/title'
import { withI18nTitle, withNavExtension, withTitle } from '@/utils/title'
const props = defineProps<{
orgId: string
contestId: string
}>()
const { t } = useI18n()
const { contest, showRegistration, showAdminTab } = useContest(
toRef(props, 'orgId'),
toRef(props, 'contestId')
)
withTitle(computed(() => contest.state.value?.title ?? t('pages.contests')))
withI18nTitle('pages.contests')
const problems = useContestProblemList(toRef(props, 'contestId'))
Expand All @@ -75,14 +73,22 @@ const participant = useAsyncState(async () => {
return resp
}, null)
const layout = useLayout()
const headerVisible = ref(true)
const rootMargin = computed(() => `-${layout.mainRect.value.top}px 0px 0px 0px`)
function onIntersect(isIntersecting: boolean) {
headerVisible.value = isIntersecting
}
withNavExtension(
ContestTabs,
computed(() => ({
orgId: props.orgId,
contestId: props.contestId,
showAdminTab: showAdminTab.value,
registered: !!participant.state.value,
contest: contest.state.value
contest: contest.state.value,
headerVisible: headerVisible.value
}))
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@
<VTab
v-for="problem of value"
:key="problem._id"
prepend-icon="mdi-list-box-outline"
:to="rel(problem._id)"
exact
prepend-icon="mdi-list-box-outline"
class="text-none"
>
{{ problem.settings.slug }}.
{{ problem.title }}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@
<VTab
v-for="contest of value"
:key="contest._id"
prepend-icon="mdi-list-box-outline"
:to="rel(contest._id)"
class="text-none"
prepend-icon="mdi-list-box-outline"
exact
>
<span>
Expand Down

0 comments on commit 5edc50b

Please sign in to comment.