{"id":31572255,"url":"https://github.com/hsuenchan/blog-admin-angular","last_synced_at":"2026-05-06T17:31:28.427Z","repository":{"id":307957471,"uuid":"1031173833","full_name":"HsuenChan/blog-admin-angular","owner":"HsuenChan","description":"A simple Angular admin dashboard project created for coursework submission. Used to demonstrate routing, component structure, and SCSS styling.","archived":false,"fork":false,"pushed_at":"2025-09-12T08:56:53.000Z","size":148,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-09-12T10:37:22.230Z","etag":null,"topics":["angular","angular-cli","angular-material","front-end","google-sheets-api","rxjs","typescript"],"latest_commit_sha":null,"homepage":"https://hsuenchan.github.io/blog-admin-angular/","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/HsuenChan.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-08-03T07:04:12.000Z","updated_at":"2025-09-12T08:56:56.000Z","dependencies_parsed_at":null,"dependency_job_id":"8c5f4f1a-4fa1-4fda-b299-7de4fce83f1d","html_url":"https://github.com/HsuenChan/blog-admin-angular","commit_stats":null,"previous_names":["hsuenchan/blog-admin-angular"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/HsuenChan/blog-admin-angular","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/HsuenChan%2Fblog-admin-angular","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/HsuenChan%2Fblog-admin-angular/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/HsuenChan%2Fblog-admin-angular/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/HsuenChan%2Fblog-admin-angular/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/HsuenChan","download_url":"https://codeload.github.com/HsuenChan/blog-admin-angular/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/HsuenChan%2Fblog-admin-angular/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":278464272,"owners_count":25991177,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-05T02:00:06.059Z","response_time":54,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["angular","angular-cli","angular-material","front-end","google-sheets-api","rxjs","typescript"],"created_at":"2025-10-05T13:50:40.491Z","updated_at":"2025-10-05T13:50:41.399Z","avatar_url":"https://github.com/HsuenChan.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# BlogAdminAngular · 部落格後台（Angular）\n\n![Angular](https://img.shields.io/badge/Angular-v20.1.4-dd0031?logo=angular\u0026logoColor=white)\n![TypeScript](https://img.shields.io/badge/TypeScript-5.x-007ACC?logo=typescript\u0026logoColor=white)\n![RxJS](https://img.shields.io/badge/RxJS-7.x-B7178C?logo=reactivex\u0026logoColor=white)\n![Angular Material](https://img.shields.io/badge/Angular%20Material-UI-blue?logo=angular\u0026logoColor=white)\n\n![GitHub Pages](https://img.shields.io/badge/Deploy-GitHub%20Pages-222222?logo=githubpages\u0026logoColor=white)\n![CI/CD](https://img.shields.io/badge/CI-CD%2FDeploy-blue?logo=githubactions\u0026logoColor=white)\n\n\n**Live Demo｜展示網站**  \nhttps://hsuenchan.github.io/blog-admin-angular/\n\n\u003e English follows each Chinese section.（下方每節皆有中英對照）\n\n---\n\n## Overview｜專案簡介\n\n**EN**  \nBlogAdminAngular is an Angular (v20.1.4) demo admin app. It showcases basic CRUD UI for blog content using Angular Material, Reactive Forms, routing/guards, and simple state handling. **Data source uses Google Sheets API** as a lightweight “DB” for demo purposes.\n\n**ZH**  \nBlogAdminAngular 是以 Angular（v20.1.4）打造的示範後台，包含文章管理的基本介面（列表 / 新增 / 編輯 / 刪除）、Angular Material、Reactive Forms、路由與守衛與簡易狀態處理。**資料來源使用 Google Sheets API**，作為輕量示範的「資料庫」。\n\n---\n\n## Tech Stack｜技術堆疊\n\n- Angular 20.1.4（CLI）\n- node version 22\n- Angular Material\n- RxJS、Reactive Forms\n- Routing \u0026 Guards\n- Google Sheets API（資料來源 / DB）\n- 部署：GitHub Pages\n\n---\n\n## Getting Started｜快速開始\n\n### 1) Development server｜本機開發\n\n```bash\nnvm use 22\nng serve\n# open http://localhost:4200/\n```\n\n**EN** Save changes and the app reloads automatically.  \n**ZH** 儲存檔案後頁面會自動重新載入。\n\n---\n\n### 2) Code scaffolding｜產生元件樣板\n\n```bash\nng generate component component-name\n# or list all schematics\nng generate --help\n```\n\n---\n\n### 3) Building｜建置\n\n```bash\nng build\n# build artifacts are output to dist/\n```\n\n**EN** Production build is optimized.  \n**ZH** Production 版本會自動優化效能與載入速度。\n\n---\n\n## Google Sheets as DB｜以 Google 試算表作為資料庫\n\n\u003e **EN**: For demo only. Don’t put secrets on client-side. For private sheets or write access, add a backend proxy (Cloud Functions/Cloud Run/etc.) and keep credentials server-side.  \n\u003e **ZH**：此作法僅供示範。**請勿**在前端放機密金鑰。若需讀寫私有試算表，請改由後端代理（如 Cloud Functions/Cloud Run）並在伺服器端保護憑證。\n\n### A) Make your Sheet readable｜建立可讀的試算表\n- **EN**\n  1. Create a Google Sheet and add demo data (e.g., headers: `id`, `title`, `content`, `updated_at`).\n  2. If using an **API key** approach (public read), set the sheet to **Anyone with the link: Viewer** or publish a specific range.\n  3. Get `spreadsheetId` from the sheet URL.\n- **ZH**\n  1. 建立試算表並加入示範資料（如欄位：`id`, `title`, `content`, `updated_at`）。\n  2. 若採 **API key** 的公開唯讀，將試算表設為「知道連結的使用者可檢視」，或發布指定範圍。\n  3. 從網址取得 `spreadsheetId`。\n\n### B) Enable API \u0026 get key｜啟用 API 與取得金鑰\n- **EN**: In Google Cloud Console → enable **Google Sheets API** and create an **API key** (for public read) or set up **OAuth/Service Account** for private sheets.  \n- **ZH**：在 Google Cloud Console 啟用 **Google Sheets API**，建立 **API key**（公開唯讀）或設定 **OAuth/Service Account**（讀寫私有表）。\n\n---\n\n## D) Service (no environment yet)｜服務呼叫範例（尚未使用 environment）\n\n\u003cpicture\u003e\n  \u003csource srcset=\"https://github.com/user-attachments/assets/e54cb608-68a0-4016-89c1-1c06dc42f92a\" media=\"(prefers-color-scheme: dark)\"\u003e\n  \u003cimg src=\"https://github.com/user-attachments/assets/0ace5f4d-361f-4158-8e18-197c3ef728f0\" alt=\"Architecture Diagram\" width=\"820\"\u003e\n\u003c/picture\u003e\n\n\n**EN**  \nEnvironment files are not set up yet. The current service uses **hard-coded values** (spreadsheetId, apiKey, Apps Script URL). After environment files are introduced (see “Roadmap / To-Do”), please move these sensitive values out of source code. See the current implementation in:  \n`src/app/google-sheets.service.ts`\n\n**ZH**  \n目前尚未建立 environment 檔，服務暫以**硬編寫參數**（試算表 ID、API Key、Apps Script URL）運作。未來完成 environment 設定後（見「開發待辦」），請將這些敏感參數移出原始碼。現行實作請見：  \n`src/app/google-sheets.service.ts`\n\n\u003e ⚠️ **Security note｜安全注意**：公開 repo 不建議硬編寫真實金鑰；若已提交，建議於 GCP 重新產生 API Key 並限制來源，或改走後端代理。\n\n#### Angular setup｜Angular 設定\n\n**EN**\n- Ensure `HttpClient` is provided:\n  - Standalone bootstrap:\n    ```ts\n    import { provideHttpClient } from '@angular/common/http'\n    bootstrapApplication(AppComponent, { providers: [provideHttpClient()] })\n    ```\n  - Or in `AppModule`:\n    ```ts\n    import { HttpClientModule } from '@angular/common/http'\n    @NgModule({ imports: [HttpClientModule] })\n    export class AppModule {}\n    ```\n- For **write/delete** via Apps Script, deploy it as **Web app** with **Who has access: Anyone** and set CORS headers if needed.\n\n**ZH**\n- 確認已提供 `HttpClient`：\n  - Standalone 啟動：\n    ```ts\n    import { provideHttpClient } from '@angular/common/http'\n    bootstrapApplication(AppComponent, { providers: [provideHttpClient()] })\n    ```\n  - 或在 `AppModule`：\n    ```ts\n    import { HttpClientModule } from '@angular/common/http'\n    @NgModule({ imports: [HttpClientModule] })\n    export class AppModule {}\n    ```\n- 透過 Apps Script 進行寫入/刪除：請以 **Web app** 方式部署，存取權選 **任何人**；若遇 CORS，請在 Apps Script 回應中加入適當的 CORS 標頭。\n\n---\n\n## Routing notes on GitHub Pages｜GitHub Pages 路由注意\n\n**EN**  \nIf deploying to `https://\u003cuser\u003e.github.io/\u003crepo\u003e/`, build with `--base-href \"/\u003crepo\u003e/\"`. For SPA routes, copy `index.html` to `404.html` in the build output to avoid 404 on refresh (already handled in the sample GitHub Actions below).  \nAlternative: use **hash routing** to avoid the 404 workaround.\n\n**ZH**  \n若部署到 `https://\u003cuser\u003e.github.io/\u003crepo\u003e/`，建置時加上 `--base-href \"/\u003crepo\u003e/\"`。SPA 在重新整理或深連結可能 404，可在輸出目錄把 `index.html` 複製成 `404.html`（下方 Actions 範例已處理）。  \n或採用 **hash 路由**，可避免 404 的額外設定。\n\n---\n\n## Deploy to GitHub Pages｜部署到 GitHub Pages（Actions）\n\n**EN**\n1. Create `.github/workflows/deploy.yml` (see below).  \n2. Build with `--base-href \"/blog-admin-angular/\"`.  \n3. Upload `dist/**/browser` (or `dist/**`) as artifact and deploy via `actions/deploy-pages`.  \n4. In **Settings → Pages**, set **Source = GitHub Actions**.\n\n**ZH**  \n1. 建立 `.github/workflows/deploy.yml`（如下）。  \n2. 建置時加入 `--base-href \"/blog-admin-angular/\"`。  \n3. 上傳 `dist/**/browser`（或 `dist/**`）並用 `actions/deploy-pages` 部署。  \n4. 於 **Settings → Pages** 將 **Source** 設為 **GitHub Actions**。\n\n```yaml\n# .github/workflows/deploy.yml\nname: Deploy Angular to GitHub Pages\n\non:\n  push:\n    branches: [ main ]   # 若預設分支是 master，請改成 master\n  workflow_dispatch:\n\npermissions:\n  contents: read\n  pages: write\n  id-token: write\n\nconcurrency:\n  group: \"pages\"\n  cancel-in-progress: true\n\njobs:\n  build:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v4\n      - uses: actions/setup-node@v4\n        with:\n          node-version: '20'\n          cache: 'npm'\n      - run: npm ci\n      - run: npm run build -- --configuration=production --base-href \"/blog-admin-angular/\"\n\n      - id: vars\n        run: |\n          if [ -d dist/*/browser ]; then\n            echo \"dir=$(ls -d dist/*/browser)\" \u003e\u003e $GITHUB_OUTPUT\n          else\n            echo \"dir=$(ls -d dist/*)\" \u003e\u003e $GITHUB_OUTPUT\n          fi\n\n      # prevent SPA refresh/DEEPLINK 404 on GH Pages\n      - run: cp \"${{ steps.vars.outputs.dir }}/index.html\" \"${{ steps.vars.outputs.dir }}/404.html\"\n\n      - uses: actions/upload-pages-artifact@v3\n        with:\n          path: ${{ steps.vars.outputs.dir }}\n\n  deploy:\n    needs: build\n    runs-on: ubuntu-latest\n    environment:\n      name: github-pages\n      url: ${{ steps.deployment.outputs.page_url }}\n    steps:\n      - id: deployment\n        uses: actions/deploy-pages@v4\n```\n\n---\n\n## Roadmap / To-Do｜開發待辦\n\n**EN**  \nTesting and environment configuration are not implemented yet. Below is the current to-do list.\n\n**ZH**  \n測試與 environment 設定尚未完成；待辦如下。\n\n- [ ] **Environment configuration（環境設定）**  \n      Add `src/environments/environment.ts` \u0026 `environment.prod.ts`, and wire `fileReplacements` in `angular.json`.  \n      Provide an `environment.example.ts` (do not commit real keys) with fields:\n      ```ts\n      // environment.example.ts\n      export const environment = {\n        production: false,\n        googleSheets: {\n          spreadsheetId: '\u003cYOUR_SPREADSHEET_ID\u003e',\n          apiKey: '\u003cYOUR_API_KEY\u003e',\n          range: 'Sheet1!A:D'\n        }\n      };\n      ```\n      Consumers copy it to `environment.ts` / `environment.prod.ts` and fill in values.\n\n- [ ] **Secrets handling（金鑰管理）**  \n      Don’t commit real API keys. For private sheets or write access, use a backend proxy (e.g., Cloud Functions/Run) and store credentials server-side. Optionally add a build script to generate `environment.*.ts` from a local `.env` (not checked in).\n\n- [ ] **Unit tests（單元測試）**：set up Jest/Karma and add initial specs  \n- [ ] **E2E tests（端到端測試）**：choose Cypress or Playwright and write basic flows  \n- [ ] **CI before deploy（部署前自動化）**：run tests \u0026 lint in GitHub Actions  \n- [ ] **Lint \u0026 format（程式風格）**：ESLint/Prettier；Husky + lint-staged pre-commit  \n- [ ] **Mock data（資料模擬）**：stub Google Sheets API responses for tests\n\n---\n\n## Additional Resources｜延伸資源\n\n- Angular CLI Overview \u0026 Command Reference  \n  https://angular.dev/tools/cli\n\n---\n\n## 📜 Meta\n\n[![License](https://img.shields.io/badge/License-MIT-green)](./LICENSE)\n![Made with Angular](https://img.shields.io/badge/Made%20with-Angular-red)\n![Made with Markdown](https://img.shields.io/badge/Docs-Markdown-1f425f.svg)\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhsuenchan%2Fblog-admin-angular","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhsuenchan%2Fblog-admin-angular","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhsuenchan%2Fblog-admin-angular/lists"}