Skip to content

Commit

Permalink
feat: 加入測試
Browse files Browse the repository at this point in the history
  • Loading branch information
yinmin8610 committed Jan 6, 2025
1 parent 73db477 commit 37c05e0
Show file tree
Hide file tree
Showing 19 changed files with 1,160 additions and 749 deletions.
980 changes: 393 additions & 587 deletions package-lock.json

Large diffs are not rendered by default.

4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,15 @@
"build": "run-p type-check \"build-only {@}\" --",
"preview": "vite preview",
"test:unit": "vitest",
"coverage": "vitest run --coverage",
"build-only": "vite build",
"type-check": "vue-tsc --build --force",
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore",
"format": "prettier --write src/"
},
"dependencies": {
"@popperjs/core": "^2.11.8",
"@vitest/coverage-v8": "^2.1.8",
"axios": "^1.7.7",
"bootstrap": "^5.3.3",
"bootstrap-icons": "^1.11.3",
Expand All @@ -40,7 +42,7 @@
"sass": "1.77.6",
"typescript": "~5.4.0",
"vite": "^5.3.1",
"vitest": "^1.6.0",
"vitest": "^2.1.8",
"vue-tsc": "^2.0.21"
}
}
6 changes: 4 additions & 2 deletions src/apis/data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import httpRequest from '../apis/httpRequest'
*
* @returns {Promise} A promise that resolves to the response data containing the list of projects.
*/
export const apiGetProjects = (): Promise<any> =>
const apiGetProjects = (): Promise<any> =>
httpRequest.http.get(
'https://raw.githubusercontent.com/hexschool/hexschoolActionAPI/master/data.json'
)
Expand All @@ -15,7 +15,9 @@ export const apiGetProjects = (): Promise<any> =>
*
* @returns {Promise} A promise that resolves to the response data containing the list of projects.
*/
export const apiGetCategories = (): Promise<any> =>
const apiGetCategories = (): Promise<any> =>
httpRequest.http.get(
'https://raw.githubusercontent.com/hexschool/hexschoolActionAPI/refs/heads/master/category.json'
)

export default { apiGetProjects, apiGetCategories }
12 changes: 7 additions & 5 deletions src/components/AppCard.vue
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
<script setup lang="ts">
import type { Ref } from 'vue';
import type { Project } from '../types/global.type';
import type { Project } from '@/types/global.type';
import { ref } from 'vue';
// icons
import IconChevronDown from './icons/IconChevronDown.vue';
import IconBehance from './icons/IconBehance.vue';
import IconGitHub from './icons/IconGitHub.vue';
import IconLink from './icons/IconLink.vue';
import IconChevronDown from '@/components/icons/IconChevronDown.vue';
import IconBehance from '@/components/icons/IconBehance.vue';
import IconGitHub from '@/components/icons/IconGitHub.vue';
import IconLink from '@/components/icons/IconLink.vue';
interface Props {
projects: Project[]
Expand Down Expand Up @@ -41,6 +41,8 @@ const onClickHandler = (id: string) => {
const isToggled = (id: string, stateId: string | null) => {
return stateId === id;
}
defineExpose({ hoveredProjectId, clickedProjectId })
</script>

<template>
Expand Down
219 changes: 120 additions & 99 deletions src/components/AppLayout.vue
Original file line number Diff line number Diff line change
@@ -1,24 +1,25 @@
<script setup lang="ts">
import type { Ref } from 'vue';
import type { CourseCategory, CourseCategoryDetail } from '../types/global.type';
import type { CourseCategory, CourseCategoryDetail } from '@/types/global.type';
import { onMounted, ref } from 'vue';
import { onMounted, ref, onUnmounted } from 'vue';
import { useRoute, useRouter } from 'vue-router';
// composables
import { useCountdown } from '../composables/countdownActivity';
import { useCountdown } from '@/composables/countdownActivity';
// apis
import { apiGetCategories } from '../apis/data';
import apis from '@/apis/data';
const { apiGetCategories } = apis;
// assets
import logo from '../assets/logo.svg';
import logo from '@/assets/logo.svg';
// icons
import IconArrowRight from './icons/IconArrorRight.vue';
import IconArrowRight from '@/components/icons/IconArrorRight.vue';
// data
import courseCategory from '../data/course-category.json';
import courseCategory from '@/data/course-category.json';
const route = useRoute();
const router = useRouter();
Expand Down Expand Up @@ -58,150 +59,170 @@ onMounted(async () => {
const onClickHandler = (course: CourseCategory) => {
// 切換 category active
toggleCategoryActive(course);
// category 的詳細資訊
getCategoryDetail(course);
// tag 選項更新
getTagOptions(course);
// 搜尋與下拉選項回到初始狀態
initStatus();
// route 更新
replaceRouter(course)
};
const toggleCategoryActive = (course: CourseCategory) => {
courseCategoryList.value = courseCategoryList.value.map((courseCategory) => ({
...courseCategory,
isActive: courseCategory.id === course.id
}));
// category 的詳細資訊
}
const getCategoryDetail = (course: CourseCategory) => {
courseCategoryDetail.value = {
...course
}
// tag 選項更新
}
const getTagOptions = (course: CourseCategory) => {
tags.value = categories.value[course.category];
// 搜尋與下拉選項回到初始狀態
}
const initStatus = () => {
searchValue.value = '';
selectedTag.value = '';
// route 更新
}
const replaceRouter = (course: CourseCategory) => {
router.replace({
query: {
...route.query,
category: course.category,
}
});
};
}
// 活動倒數公告
const deadline = 'Nov 13 2024 23:59:59';
const { formattedTime } = useCountdown(deadline);
const { formattedTime, interval, startCountdown } = useCountdown(deadline);
onMounted(startCountdown);
onUnmounted(() => {
if (interval.value) clearInterval(interval.value);
});
</script>

<template>
<header class="py-3 border-bottom">
<div class="container">
<router-link to="/" class="d-flex align-items-center">
<img :src="logo" alt="六角學院作品牆" width="32" height="32">
<h1 class="h6 mb-0">六角學院作品牆</h1>
</router-link>
</div>
</header>

<nav class="py-4">
<ul class="nav nav-pills justify-content-start justify-content-md-center flex-nowrap overflow-auto scrollBar">
<li v-for="courseCategory in courseCategoryList" :key="courseCategory.id" class="nav-item">
<a href="#" class="nav-link fs-5" :class="{ 'active': courseCategory.isActive }"
@click.prevent="onClickHandler(courseCategory)">
{{ courseCategory.name }}
</a>
</li>
</ul>
</nav>

<section class="py-6">
<h2 class="h4 text-center fw-bold mb-3">{{ courseCategoryDetail.name }}</h2>
<p class="text-center fw-normal mb-3">
{{ courseCategoryDetail.description }}
</p>
<div class="d-flex justify-content-center">
<div class="mx-1" v-for="link in courseCategoryDetail.relLinks" :key="link.routeName">
<a :href="link.url" class="btn btn-outline-secondary" target="_blank">{{ link.urlText }}</a>
<div>
<header class="py-3 border-bottom">
<div class="container">
<router-link to="/" class="d-flex align-items-center">
<img :src="logo" alt="六角學院作品牆" width="32" height="32">
<h1 class="h6 mb-0">六角學院作品牆</h1>
</router-link>
</div>
</div>
</section>
</header>

<nav class="py-4">
<ul class="nav nav-pills justify-content-start justify-content-md-center flex-nowrap overflow-auto scrollBar">
<li v-for="courseCategory in courseCategoryList" :key="courseCategory.id" class="nav-item">
<a href="#" class="nav-link fs-5" :class="{ 'active': courseCategory.isActive }"
@click.prevent="onClickHandler(courseCategory)">
{{ courseCategory.name }}
</a>
</li>
</ul>
</nav>

<section class="py-6">
<h2 class="h4 text-center fw-bold mb-3">{{ courseCategoryDetail.name }}</h2>
<p class="text-center fw-normal mb-3">
{{ courseCategoryDetail.description }}
</p>
<div class="d-flex justify-content-center">
<div class="mx-1" v-for="link in courseCategoryDetail.relLinks" :key="link.routeName">
<a :href="link.url" class="btn btn-outline-secondary" target="_blank">{{ link.urlText }}</a>
</div>
</div>
</section>

<form class="container">
<div class="row justify-content-center align-items-center py-3 gap-2 gap-md-0">
<div class="col-md-4 mb-md-0">
<input class="form-control rounded-pill" v-model="searchValue" type="search" placeholder="搜尋此標籤下的作品"
aria-label="Search" />
<form class="container">
<div class="row justify-content-center align-items-center py-3 gap-2 gap-md-0">
<div class="col-md-4 mb-md-0">
<input class="form-control rounded-pill" v-model="searchValue" type="search" placeholder="搜尋此標籤下的作品"
aria-label="Search" />
</div>
<div class="col-md-2 mb-md-0">
<select class="form-select rounded-pill" v-model="selectedTag">
<option disabled selected>選擇此標籤課程</option>
<option value="">全部</option>
<option v-for="(tag, id) in tags" :key="id" :value="tag">
{{ tag }}
</option>
</select>
</div>
</div>
<div class="col-md-2 mb-md-0">
<select class="form-select rounded-pill" v-model="selectedTag">
<option disabled selected>選擇此標籤課程</option>
<option value="">全部</option>
<option v-for="(tag, id) in tags" :key="id" :value="tag">
{{ tag }}
</option>
</select>
</form>

<router-view
:filterCourses="{ category: courseCategoryDetail.category, searchValue, tag: selectedTag }"></router-view>

<footer class="py-3 mt-5" :class="{ 'pb-6': formattedTime }">
<div class="container">
<p class="d-flex flex-column flex-md-row justify-content-md-between">
<small class="fw-normal">
<a href="mailto:[email protected]">聯絡我們</a>
</small>
<small class="fw-normal">
Copyright © 2024 HexSchool.All rights reserved.
</small>
</p>
</div>
</div>
</form>

<router-view
:filterCourses="{ category: courseCategoryDetail.category, searchValue, tag: selectedTag }"></router-view>

<footer class="py-3 mt-5" :class="{ 'pb-6': formattedTime }">
<div class="container">
<p class="d-flex flex-column flex-md-row justify-content-md-between">
<small class="fw-normal">
<a href="mailto:[email protected]">聯絡我們</a>
</small>
<small class="fw-normal">
Copyright © 2024 HexSchool.All rights reserved.
</small>
</p>
</div>
</footer>
<div class="
</footer>
<div class="
text-center text-white
fixed-bottom
bg-danger
px-2
py-2
" v-if="formattedTime">

<div class="
<div class="
d-flex
align-items-center
justify-content-md-center justify-content-between
">
<div class="
<div class="
d-flex
flex-md-row flex-column
text-white
justify-content-md-end
pe-md-3 pe-0
">
<span class="me-md-5 fw-bold text-start">【熱烈報名中】
後端工程師-資料庫體驗營
<!-- <br class="d-md-none d-block" />|2024 切版直播班 -->
</span>

<div class="d-flex justify-content-start text-white text-nowrap">
<span class="fw-bold">
{{ formattedTime }}
<span class="me-md-5 fw-bold text-start">【熱烈報名中】
後端工程師-資料庫體驗營
<!-- <br class="d-md-none d-block" />|2024 切版直播班 -->
</span>

<div class="d-flex justify-content-start text-white text-nowrap">
<span class="fw-bold">
{{ formattedTime }}
</span>
</div>
</div>
</div>
<div class="
<div class="
d-flex
justify-content-md-start justify-content-end
ps-md-3 ps-0
">
<a href="https://www.hexschool.com/2024/10/02/2024-10-02-backend-database-camping/" target="_blank"
class="btn btn-dark btn-sm rounded-0">
立即享優惠
<IconArrowRight />
</a>
<a href="https://www.hexschool.com/2024/10/02/2024-10-02-backend-database-camping/" target="_blank"
class="btn btn-dark btn-sm rounded-0">
立即享優惠
<IconArrowRight />
</a>
</div>
</div>
</div>

</div>
</div>
</template>

Expand Down
3 changes: 2 additions & 1 deletion src/components/AppPagination.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<script setup lang="ts">
import type { Ref } from 'vue';
import type { Project } from '../types/global.type';
import type { Project } from '@/types/global.type';
import { ref, watch } from "vue";
import { useRoute } from 'vue-router';
Expand Down Expand Up @@ -35,6 +35,7 @@ watch(
}
);
defineExpose({ currentPage })
</script>
<template>
<div class="d-flex justify-content-center py-5">
Expand Down
Loading

0 comments on commit 37c05e0

Please sign in to comment.