From fb26ab1e99263ba6f953bd4efc05d8b11d5df7fb Mon Sep 17 00:00:00 2001 From: Gus Date: Thu, 2 Oct 2025 17:14:18 +0800 Subject: [PATCH 01/19] Keep project skeletons consistent --- src/pages/ProjectsPage.vue | 15 +++++++-------- src/partials/ProjectCardPartial.vue | 2 +- src/partials/ProjectCardSkeletonPartial.vue | 8 ++++---- 3 files changed, 12 insertions(+), 13 deletions(-) diff --git a/src/pages/ProjectsPage.vue b/src/pages/ProjectsPage.vue index 1171479c..dc7d21b7 100644 --- a/src/pages/ProjectsPage.vue +++ b/src/pages/ProjectsPage.vue @@ -31,14 +31,13 @@

Open Source / Client Projects

- - -

No projects are available at the moment. Please check back soon.

-
+ + +
diff --git a/src/partials/ProjectCardPartial.vue b/src/partials/ProjectCardPartial.vue index fe364fca..dab067cb 100644 --- a/src/partials/ProjectCardPartial.vue +++ b/src/partials/ProjectCardPartial.vue @@ -6,7 +6,7 @@ target="_blank" rel="noopener noreferrer" > -
+
diff --git a/src/partials/ProjectCardSkeletonPartial.vue b/src/partials/ProjectCardSkeletonPartial.vue index 61431031..f9c71f69 100644 --- a/src/partials/ProjectCardSkeletonPartial.vue +++ b/src/partials/ProjectCardSkeletonPartial.vue @@ -1,6 +1,6 @@ From ebb62ab9de36fd16b4ac8e7fb800342024c45d41 Mon Sep 17 00:00:00 2001 From: Gus Date: Thu, 2 Oct 2025 17:26:52 +0800 Subject: [PATCH 03/19] Add skill widget skeleton and align talk loader layout --- src/pages/AboutPage.vue | 15 ++++++++---- src/pages/HomePage.vue | 25 ++++++++++++-------- src/pages/ProjectsPage.vue | 14 +++++++---- src/pages/ResumePage.vue | 15 ++++++++---- src/partials/TalkCardSkeletonPartial.vue | 8 +++---- src/partials/WidgetSkillsSkeletonPartial.vue | 18 ++++++++++++++ 6 files changed, 66 insertions(+), 29 deletions(-) create mode 100644 src/partials/WidgetSkillsSkeletonPartial.vue diff --git a/src/pages/AboutPage.vue b/src/pages/AboutPage.vue index 489ba43b..f27b71dd 100644 --- a/src/pages/AboutPage.vue +++ b/src/pages/AboutPage.vue @@ -67,8 +67,9 @@
@@ -88,6 +89,7 @@ import HeaderPartial from '@partials/HeaderPartial.vue'; import SideNavPartial from '@partials/SideNavPartial.vue'; import WidgetSocialPartial from '@partials/WidgetSocialPartial.vue'; import WidgetSkillsPartial from '@partials/WidgetSkillsPartial.vue'; +import WidgetSkillsSkeletonPartial from '@partials/WidgetSkillsSkeletonPartial.vue'; import { useSeo, SITE_NAME, ABOUT_IMAGE, siteUrlFor, buildKeywords, PERSON_JSON_LD } from '@/support/seo'; import { useApiStore } from '@api/store.ts'; @@ -97,6 +99,7 @@ import type { ProfileResponse } from '@api/response/index.ts'; const apiStore = useApiStore(); const nickname = ref('Gus'); const profile = ref(null); +const isLoadingProfile = ref(true); const aboutPicture = computed(() => { return AboutPicture; @@ -135,8 +138,10 @@ onMounted(async () => { profile.value = userProfileResponse.data; nickname.value = profile.value.nickname; } - } catch (error) { - debugError(error); - } + } catch (error) { + debugError(error); + } finally { + isLoadingProfile.value = false; + } }); diff --git a/src/pages/HomePage.vue b/src/pages/HomePage.vue index 2efe7e48..99ca8c76 100644 --- a/src/pages/HomePage.vue +++ b/src/pages/HomePage.vue @@ -25,8 +25,9 @@
@@ -48,6 +49,7 @@ import WidgetSkillsPartial from '@partials/WidgetSkillsPartial.vue'; import ArticlesListPartial from '@partials/ArticlesListPartial.vue'; import WidgetSponsorPartial from '@partials/WidgetSponsorPartial.vue'; import FeaturedProjectsPartial from '@partials/FeaturedProjectsPartial.vue'; +import WidgetSkillsSkeletonPartial from '@partials/WidgetSkillsSkeletonPartial.vue'; import { onMounted, ref } from 'vue'; import { useApiStore } from '@api/store.ts'; @@ -57,6 +59,7 @@ import { useSeo, SITE_NAME, ABOUT_IMAGE, siteUrlFor, buildKeywords, PERSON_JSON_ const apiStore = useApiStore(); const profile = ref(null); +const isLoadingProfile = ref(true); useSeo({ title: 'Home', @@ -78,14 +81,16 @@ useSeo({ }); onMounted(async () => { - try { - const userProfileResponse = await apiStore.getProfile(); + try { + const userProfileResponse = await apiStore.getProfile(); - if (userProfileResponse.data) { - profile.value = userProfileResponse.data; - } - } catch (error) { - debugError(error); - } + if (userProfileResponse.data) { + profile.value = userProfileResponse.data; + } + } catch (error) { + debugError(error); + } finally { + isLoadingProfile.value = false; + } }); diff --git a/src/pages/ProjectsPage.vue b/src/pages/ProjectsPage.vue index dc7d21b7..b4c7e8d4 100644 --- a/src/pages/ProjectsPage.vue +++ b/src/pages/ProjectsPage.vue @@ -47,8 +47,9 @@
@@ -70,6 +71,7 @@ import SideNavPartial from '@partials/SideNavPartial.vue'; import ProjectCardPartial from '@partials/ProjectCardPartial.vue'; import WidgetSkillsPartial from '@partials/WidgetSkillsPartial.vue'; import WidgetSponsorPartial from '@partials/WidgetSponsorPartial.vue'; +import WidgetSkillsSkeletonPartial from '@partials/WidgetSkillsSkeletonPartial.vue'; import type { ProfileResponse, ProjectsResponse } from '@api/response/index.ts'; import ProjectCardSkeletonPartial from '@partials/ProjectCardSkeletonPartial.vue'; import { useSeo, SITE_NAME, ABOUT_IMAGE, siteUrlFor, buildKeywords, PERSON_JSON_LD } from '@/support/seo'; @@ -78,6 +80,7 @@ const apiStore = useApiStore(); const isLoadingProjects = ref(true); const projects = ref([]); const profile = ref(null); +const isLoadingProfile = ref(true); useSeo({ title: 'Projects', @@ -111,8 +114,9 @@ onMounted(async () => { } } catch (error) { debugError(error); - } finally { - isLoadingProjects.value = false; - } + } finally { + isLoadingProjects.value = false; + isLoadingProfile.value = false; + } }); diff --git a/src/pages/ResumePage.vue b/src/pages/ResumePage.vue index 406be242..6a2e4cae 100644 --- a/src/pages/ResumePage.vue +++ b/src/pages/ResumePage.vue @@ -44,8 +44,9 @@
@@ -65,6 +66,7 @@ import EducationPartial from '@partials/EducationPartial.vue'; import ExperiencePartial from '@partials/ExperiencePartial.vue'; import WidgetLangPartial from '@partials/WidgetLangPartial.vue'; import WidgetSkillsPartial from '@partials/WidgetSkillsPartial.vue'; +import WidgetSkillsSkeletonPartial from '@partials/WidgetSkillsSkeletonPartial.vue'; import RecommendationPartial from '@partials/RecommendationPartial.vue'; import { ref, onMounted } from 'vue'; @@ -81,6 +83,7 @@ const navigationItems = [ const apiStore = useApiStore(); const profile = ref(null); +const isLoadingProfile = ref(true); const education = ref(null); const experience = ref(null); const recommendations = ref(null); @@ -128,8 +131,10 @@ onMounted(async () => { if (educationResponse.data) { education.value = educationResponse.data; } - } catch (error) { - debugError(error); - } + } catch (error) { + debugError(error); + } finally { + isLoadingProfile.value = false; + } }); diff --git a/src/partials/TalkCardSkeletonPartial.vue b/src/partials/TalkCardSkeletonPartial.vue index 88639a7e..b47e773c 100644 --- a/src/partials/TalkCardSkeletonPartial.vue +++ b/src/partials/TalkCardSkeletonPartial.vue @@ -1,11 +1,11 @@