Merge pull request #59 from HideyoshiNakazone/implements-projects-page
Implementation of Projects Page
This commit is contained in:
27
.github/workflows/vercel-cleanup-pr.yml
vendored
Normal file
27
.github/workflows/vercel-cleanup-pr.yml
vendored
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
name: vercel-cleanup-pr
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
types: [closed]
|
||||||
|
|
||||||
|
env:
|
||||||
|
VERCEL_CLI_TOKEN: ${{ secrets.VERCEL_CLI_TOKEN }}
|
||||||
|
VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}
|
||||||
|
GITHUB_PR_ID: ${{ github.event.number }}
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
cleanup:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Cleanup Vercel Deployments
|
||||||
|
run: |
|
||||||
|
closed_deployments=$(curl "https://api.vercel.com/v6/deployments?projectId=$VERCEL_PROJECT_ID" \
|
||||||
|
-H "Accept: application/json" \
|
||||||
|
-H "Authorization: Bearer ${VERCEL_CLI_TOKEN}" | jq -r ".deployments[] | select(.meta.githubPrId == \"${GITHUB_PR_ID}\") | .uid")
|
||||||
|
for deployment in $closed_deployments; do
|
||||||
|
echo "Deleting Deployment: $deployment"
|
||||||
|
curl "https://api.vercel.com/v6/now/deployments/$deployment" \
|
||||||
|
-X DELETE \
|
||||||
|
-H "Authorization: Bearer ${VERCEL_CLI_TOKEN}"
|
||||||
|
done
|
||||||
|
|
||||||
33
.github/workflows/vercel-cleanup-previous-preview.yml
vendored
Normal file
33
.github/workflows/vercel-cleanup-previous-preview.yml
vendored
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
name: vercel-cleanup-preview
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- '*'
|
||||||
|
- '!main'
|
||||||
|
- '!devel'
|
||||||
|
|
||||||
|
env:
|
||||||
|
VERCEL_CLI_TOKEN: ${{ secrets.VERCEL_CLI_TOKEN }}
|
||||||
|
VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}
|
||||||
|
GIT_PREVIOS_COMMIT: ${{ github.event.before }}
|
||||||
|
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
cleanup:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- name: Cleanup Vercel Deployments
|
||||||
|
run: |
|
||||||
|
|
||||||
|
invalid_deployments=$(curl "https://api.vercel.com/v6/deployments?projectId=$VERCEL_PROJECT_ID" \
|
||||||
|
-H "Accept: application/json" \
|
||||||
|
-H "Authorization: Bearer ${VERCEL_CLI_TOKEN}" | jq -r ".deployments[] | select(.meta.githubCommitSha == \"${GIT_PREVIOS_COMMIT}\") | .uid")
|
||||||
|
|
||||||
|
for deployment in $invalid_deployments; do
|
||||||
|
echo "Deleting Deployment: $deployment"
|
||||||
|
curl "https://api.vercel.com/v6/now/deployments/$deployment" \
|
||||||
|
-X DELETE \
|
||||||
|
-H "Authorization: Bearer ${VERCEL_CLI_TOKEN}"
|
||||||
|
done
|
||||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -16,6 +16,7 @@ yarn-error.log
|
|||||||
.project
|
.project
|
||||||
.classpath
|
.classpath
|
||||||
.c9/
|
.c9/
|
||||||
|
.nx/
|
||||||
*.launch
|
*.launch
|
||||||
.settings/
|
.settings/
|
||||||
*.sublime-workspace
|
*.sublime-workspace
|
||||||
@@ -45,3 +46,5 @@ Thumbs.db
|
|||||||
src/assets/env.js
|
src/assets/env.js
|
||||||
|
|
||||||
.env
|
.env
|
||||||
|
|
||||||
|
.secret
|
||||||
|
|||||||
118
package-lock.json
generated
118
package-lock.json
generated
@@ -25,12 +25,14 @@
|
|||||||
"@fortawesome/free-regular-svg-icons": "^6.1.1",
|
"@fortawesome/free-regular-svg-icons": "^6.1.1",
|
||||||
"@fortawesome/free-solid-svg-icons": "^6.1.1",
|
"@fortawesome/free-solid-svg-icons": "^6.1.1",
|
||||||
"@glidejs/glide": "^3.6.0",
|
"@glidejs/glide": "^3.6.0",
|
||||||
|
"apexcharts": "^3.45.1",
|
||||||
"bootstrap": "^4.6.2",
|
"bootstrap": "^4.6.2",
|
||||||
"cookieconsent": "^3.1.1",
|
"cookieconsent": "^3.1.1",
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
"envsub": "^4.1.0",
|
"envsub": "^4.1.0",
|
||||||
"express": "^4.18.1",
|
"express": "^4.18.1",
|
||||||
"jquery": "^3.6.0",
|
"jquery": "^3.6.0",
|
||||||
|
"ng-apexcharts": "^1.8.0",
|
||||||
"ngx-cookie-service": "^16.0.1",
|
"ngx-cookie-service": "^16.0.1",
|
||||||
"ngx-cookieconsent": "^4.0.2",
|
"ngx-cookieconsent": "^4.0.2",
|
||||||
"ngx-glide": "^16.0.0",
|
"ngx-glide": "^16.0.0",
|
||||||
@@ -5440,6 +5442,11 @@
|
|||||||
"node": ">=14.15.0"
|
"node": ">=14.15.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@yr/monotone-cubic-spline": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@yr/monotone-cubic-spline/-/monotone-cubic-spline-1.0.3.tgz",
|
||||||
|
"integrity": "sha512-FQXkOta0XBSUPHndIKON2Y9JeQz5ZeMqLYZVVK93FliNBFm7LNMIZmY6FrMEB9XPcDbE2bekMbZD6kzDkxwYjA=="
|
||||||
|
},
|
||||||
"node_modules/@zkochan/js-yaml": {
|
"node_modules/@zkochan/js-yaml": {
|
||||||
"version": "0.0.6",
|
"version": "0.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/@zkochan/js-yaml/-/js-yaml-0.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/@zkochan/js-yaml/-/js-yaml-0.0.6.tgz",
|
||||||
@@ -5722,6 +5729,20 @@
|
|||||||
"node": ">= 8"
|
"node": ">= 8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/apexcharts": {
|
||||||
|
"version": "3.45.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/apexcharts/-/apexcharts-3.45.1.tgz",
|
||||||
|
"integrity": "sha512-pPjj/SA6dfPvR/IKRZF0STdfBGpBh3WRt7K0DFuW9P8erypYkX17EHu3/molPRfo2zSiQwTVpshHC5ncysqfkA==",
|
||||||
|
"dependencies": {
|
||||||
|
"@yr/monotone-cubic-spline": "^1.0.3",
|
||||||
|
"svg.draggable.js": "^2.2.2",
|
||||||
|
"svg.easing.js": "^2.0.0",
|
||||||
|
"svg.filter.js": "^2.0.2",
|
||||||
|
"svg.pathmorphing.js": "^0.1.3",
|
||||||
|
"svg.resize.js": "^1.4.3",
|
||||||
|
"svg.select.js": "^3.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/aproba": {
|
"node_modules/aproba": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz",
|
||||||
@@ -12063,6 +12084,20 @@
|
|||||||
"resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz",
|
"resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz",
|
||||||
"integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw=="
|
"integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw=="
|
||||||
},
|
},
|
||||||
|
"node_modules/ng-apexcharts": {
|
||||||
|
"version": "1.8.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ng-apexcharts/-/ng-apexcharts-1.8.0.tgz",
|
||||||
|
"integrity": "sha512-NwJuMLHoLm52LSzM08RXV6oOOTyUYREAV53WHVGs+L2qi8UWbxCz19hX0kk+F/xFLEhhuiLegO3T1v30jLbKSQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": "^2.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@angular/common": ">=13.0.0",
|
||||||
|
"@angular/core": ">=13.0.0",
|
||||||
|
"apexcharts": "^3.41.0",
|
||||||
|
"rxjs": "^6.5.3 || ^7.4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/ngx-cookie-service": {
|
"node_modules/ngx-cookie-service": {
|
||||||
"version": "16.0.1",
|
"version": "16.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/ngx-cookie-service/-/ngx-cookie-service-16.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/ngx-cookie-service/-/ngx-cookie-service-16.0.1.tgz",
|
||||||
@@ -15233,6 +15268,89 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/svg.draggable.js": {
|
||||||
|
"version": "2.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/svg.draggable.js/-/svg.draggable.js-2.2.2.tgz",
|
||||||
|
"integrity": "sha512-JzNHBc2fLQMzYCZ90KZHN2ohXL0BQJGQimK1kGk6AvSeibuKcIdDX9Kr0dT9+UJ5O8nYA0RB839Lhvk4CY4MZw==",
|
||||||
|
"dependencies": {
|
||||||
|
"svg.js": "^2.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.8.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/svg.easing.js": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/svg.easing.js/-/svg.easing.js-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-//ctPdJMGy22YoYGV+3HEfHbm6/69LJUTAqI2/5qBvaNHZ9uUFVC82B0Pl299HzgH13rKrBgi4+XyXXyVWWthA==",
|
||||||
|
"dependencies": {
|
||||||
|
"svg.js": ">=2.3.x"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.8.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/svg.filter.js": {
|
||||||
|
"version": "2.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/svg.filter.js/-/svg.filter.js-2.0.2.tgz",
|
||||||
|
"integrity": "sha512-xkGBwU+dKBzqg5PtilaTb0EYPqPfJ9Q6saVldX+5vCRy31P6TlRCP3U9NxH3HEufkKkpNgdTLBJnmhDHeTqAkw==",
|
||||||
|
"dependencies": {
|
||||||
|
"svg.js": "^2.2.5"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.8.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/svg.js": {
|
||||||
|
"version": "2.7.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/svg.js/-/svg.js-2.7.1.tgz",
|
||||||
|
"integrity": "sha512-ycbxpizEQktk3FYvn/8BH+6/EuWXg7ZpQREJvgacqn46gIddG24tNNe4Son6omdXCnSOaApnpZw6MPCBA1dODA=="
|
||||||
|
},
|
||||||
|
"node_modules/svg.pathmorphing.js": {
|
||||||
|
"version": "0.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/svg.pathmorphing.js/-/svg.pathmorphing.js-0.1.3.tgz",
|
||||||
|
"integrity": "sha512-49HWI9X4XQR/JG1qXkSDV8xViuTLIWm/B/7YuQELV5KMOPtXjiwH4XPJvr/ghEDibmLQ9Oc22dpWpG0vUDDNww==",
|
||||||
|
"dependencies": {
|
||||||
|
"svg.js": "^2.4.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.8.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/svg.resize.js": {
|
||||||
|
"version": "1.4.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/svg.resize.js/-/svg.resize.js-1.4.3.tgz",
|
||||||
|
"integrity": "sha512-9k5sXJuPKp+mVzXNvxz7U0uC9oVMQrrf7cFsETznzUDDm0x8+77dtZkWdMfRlmbkEEYvUn9btKuZ3n41oNA+uw==",
|
||||||
|
"dependencies": {
|
||||||
|
"svg.js": "^2.6.5",
|
||||||
|
"svg.select.js": "^2.1.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.8.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/svg.resize.js/node_modules/svg.select.js": {
|
||||||
|
"version": "2.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/svg.select.js/-/svg.select.js-2.1.2.tgz",
|
||||||
|
"integrity": "sha512-tH6ABEyJsAOVAhwcCjF8mw4crjXSI1aa7j2VQR8ZuJ37H2MBUbyeqYr5nEO7sSN3cy9AR9DUwNg0t/962HlDbQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"svg.js": "^2.2.5"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.8.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/svg.select.js": {
|
||||||
|
"version": "3.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/svg.select.js/-/svg.select.js-3.0.1.tgz",
|
||||||
|
"integrity": "sha512-h5IS/hKkuVCbKSieR9uQCj9w+zLHoPh+ce19bBYyqF53g6mnPB8sAtIbe1s9dh2S2fCmYX2xel1Ln3PJBbK4kw==",
|
||||||
|
"dependencies": {
|
||||||
|
"svg.js": "^2.6.5"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.8.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/symbol-observable": {
|
"node_modules/symbol-observable": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz",
|
||||||
|
|||||||
@@ -33,12 +33,14 @@
|
|||||||
"@fortawesome/free-regular-svg-icons": "^6.1.1",
|
"@fortawesome/free-regular-svg-icons": "^6.1.1",
|
||||||
"@fortawesome/free-solid-svg-icons": "^6.1.1",
|
"@fortawesome/free-solid-svg-icons": "^6.1.1",
|
||||||
"@glidejs/glide": "^3.6.0",
|
"@glidejs/glide": "^3.6.0",
|
||||||
|
"apexcharts": "^3.45.1",
|
||||||
"bootstrap": "^4.6.2",
|
"bootstrap": "^4.6.2",
|
||||||
"cookieconsent": "^3.1.1",
|
"cookieconsent": "^3.1.1",
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
"envsub": "^4.1.0",
|
"envsub": "^4.1.0",
|
||||||
"express": "^4.18.1",
|
"express": "^4.18.1",
|
||||||
"jquery": "^3.6.0",
|
"jquery": "^3.6.0",
|
||||||
|
"ng-apexcharts": "^1.8.0",
|
||||||
"ngx-cookie-service": "^16.0.1",
|
"ngx-cookie-service": "^16.0.1",
|
||||||
"ngx-cookieconsent": "^4.0.2",
|
"ngx-cookieconsent": "^4.0.2",
|
||||||
"ngx-glide": "^16.0.0",
|
"ngx-glide": "^16.0.0",
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
import { RouterModule, Routes } from '@angular/router';
|
import { RouterModule, Routes } from '@angular/router';
|
||||||
import { HomeComponent } from './home/home.component';
|
|
||||||
import { CallbackComponent } from './header/header-popup/callback/callback.component';
|
import { CallbackComponent } from './header/header-popup/callback/callback.component';
|
||||||
|
|
||||||
|
|
||||||
const routes: Routes = [
|
const routes: Routes = [
|
||||||
{
|
{
|
||||||
path: '',
|
path: '',
|
||||||
@@ -12,7 +12,11 @@ const routes: Routes = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'home',
|
path: 'home',
|
||||||
component: HomeComponent,
|
loadChildren: () => import('./home/home.module').then(mod => mod.HomeModule),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'projects',
|
||||||
|
loadChildren: () => import('./projects/projects.module').then(mod => mod.ProjectsModule),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'callback',
|
path: 'callback',
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Component, OnDestroy, OnInit } from '@angular/core';
|
import { Component, OnDestroy, OnInit } from '@angular/core';
|
||||||
import { AuthService } from './shared/auth/auth.service';
|
import { AuthService } from './shared/service/auth.service';
|
||||||
import { UpdateService } from './shared/service-worker/update.service';
|
import { UpdateService } from './shared/service-worker/update.service';
|
||||||
import {
|
import {
|
||||||
NgcCookieConsentService,
|
NgcCookieConsentService,
|
||||||
|
|||||||
@@ -4,21 +4,18 @@ import { BrowserModule } from '@angular/platform-browser';
|
|||||||
import { AppComponent } from './app.component';
|
import { AppComponent } from './app.component';
|
||||||
import { HeaderModule } from './header/header.module';
|
import { HeaderModule } from './header/header.module';
|
||||||
import { SharedModule } from './shared/shared.module';
|
import { SharedModule } from './shared/shared.module';
|
||||||
import { HomeComponent } from './home/home.component';
|
|
||||||
import { AppRouterModule } from './app-router.module';
|
import { AppRouterModule } from './app-router.module';
|
||||||
import { FontAwesomeModule } from '@fortawesome/angular-fontawesome';
|
import { FontAwesomeModule } from '@fortawesome/angular-fontawesome';
|
||||||
import { AppServiceWorkerModule } from './app-service-worker.module';
|
import { AppServiceWorkerModule } from './app-service-worker.module';
|
||||||
import { ServiceWorkerModule } from '@angular/service-worker';
|
import { ServiceWorkerModule } from '@angular/service-worker';
|
||||||
import { environment } from '../environments/environment';
|
import { environment } from '../environments/environment';
|
||||||
import { FooterComponent } from './footer/footer.component';
|
import { FooterComponent } from './footer/footer.component';
|
||||||
import {HomeModule} from "./home/home.module";
|
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [AppComponent, FooterComponent],
|
declarations: [AppComponent, FooterComponent],
|
||||||
imports: [
|
imports: [
|
||||||
BrowserModule,
|
BrowserModule,
|
||||||
HeaderModule,
|
HeaderModule,
|
||||||
HomeModule,
|
|
||||||
AppRouterModule,
|
AppRouterModule,
|
||||||
AppServiceWorkerModule,
|
AppServiceWorkerModule,
|
||||||
SharedModule,
|
SharedModule,
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ import {
|
|||||||
faUser,
|
faUser,
|
||||||
} from '@fortawesome/free-solid-svg-icons';
|
} from '@fortawesome/free-solid-svg-icons';
|
||||||
import { Subscription } from 'rxjs';
|
import { Subscription } from 'rxjs';
|
||||||
import { AuthService } from 'src/app/shared/auth/auth.service';
|
import { AuthService } from 'src/app/shared/service/auth.service';
|
||||||
import { User } from '../../shared/model/user/user.model';
|
import { User } from '../../shared/model/user/user.model';
|
||||||
import UserChecker from '../../shared/model/user/user.checker';
|
import UserChecker from '../../shared/model/user/user.checker';
|
||||||
import { HelpComponent } from '../header-popup/help/help.component';
|
import { HelpComponent } from '../header-popup/help/help.component';
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { ActivatedRoute, Router } from '@angular/router';
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
import { Component, OnInit } from '@angular/core';
|
import { Component, OnInit } from '@angular/core';
|
||||||
import { AuthService } from 'src/app/shared/auth/auth.service';
|
import { AuthService } from 'src/app/shared/service/auth.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-callback',
|
selector: 'app-callback',
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import { MatIconRegistry } from '@angular/material/icon';
|
|||||||
import { DomSanitizer } from '@angular/platform-browser';
|
import { DomSanitizer } from '@angular/platform-browser';
|
||||||
import { faLock, faUser } from '@fortawesome/free-solid-svg-icons';
|
import { faLock, faUser } from '@fortawesome/free-solid-svg-icons';
|
||||||
import { Subscription } from 'rxjs';
|
import { Subscription } from 'rxjs';
|
||||||
import { AuthService } from 'src/app/shared/auth/auth.service';
|
import { AuthService } from 'src/app/shared/service/auth.service';
|
||||||
import { HttpError } from 'src/app/shared/model/httpError/httpError.model';
|
import { HttpError } from 'src/app/shared/model/httpError/httpError.model';
|
||||||
import HttpErrorChecker from 'src/app/shared/model/httpError/httpErrorChecker';
|
import HttpErrorChecker from 'src/app/shared/model/httpError/httpErrorChecker';
|
||||||
import UserChecker from 'src/app/shared/model/user/user.checker';
|
import UserChecker from 'src/app/shared/model/user/user.checker';
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import {
|
|||||||
OnInit,
|
OnInit,
|
||||||
Output,
|
Output,
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
import { AuthService } from '../../../shared/auth/auth.service';
|
import { AuthService } from '../../../shared/service/auth.service';
|
||||||
import { User } from '../../../shared/model/user/user.model';
|
import { User } from '../../../shared/model/user/user.model';
|
||||||
import {
|
import {
|
||||||
animate,
|
animate,
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Component, EventEmitter, Output } from '@angular/core';
|
import { Component, EventEmitter, Output } from '@angular/core';
|
||||||
import { AuthService } from '../../../../shared/auth/auth.service';
|
import { AuthService } from '../../../../shared/service/auth.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-profile-picture-picker',
|
selector: 'app-profile-picture-picker',
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import {
|
|||||||
faUser,
|
faUser,
|
||||||
} from '@fortawesome/free-solid-svg-icons';
|
} from '@fortawesome/free-solid-svg-icons';
|
||||||
import { Subscription } from 'rxjs';
|
import { Subscription } from 'rxjs';
|
||||||
import { AuthService } from 'src/app/shared/auth/auth.service';
|
import { AuthService } from 'src/app/shared/service/auth.service';
|
||||||
import { HttpError } from 'src/app/shared/model/httpError/httpError.model';
|
import { HttpError } from 'src/app/shared/model/httpError/httpError.model';
|
||||||
import HttpErrorChecker from 'src/app/shared/model/httpError/httpErrorChecker';
|
import HttpErrorChecker from 'src/app/shared/model/httpError/httpErrorChecker';
|
||||||
import UserChecker from 'src/app/shared/model/user/user.checker';
|
import UserChecker from 'src/app/shared/model/user/user.checker';
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import { faUser } from '@fortawesome/free-solid-svg-icons';
|
|||||||
import { SliderItemComponent } from 'src/app/shared/components/slider-item/slider-item.component';
|
import { SliderItemComponent } from 'src/app/shared/components/slider-item/slider-item.component';
|
||||||
import UserChecker from '../../../shared/model/user/user.checker';
|
import UserChecker from '../../../shared/model/user/user.checker';
|
||||||
import { User } from '../../../shared/model/user/user.model';
|
import { User } from '../../../shared/model/user/user.model';
|
||||||
import { AuthService } from '../../../shared/auth/auth.service';
|
import { AuthService } from '../../../shared/service/auth.service';
|
||||||
import { Subscription } from 'rxjs';
|
import { Subscription } from 'rxjs';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Component, EventEmitter, OnInit, Output } from '@angular/core';
|
import { Component, EventEmitter, OnInit, Output } from '@angular/core';
|
||||||
import { Subscription } from 'rxjs';
|
import { Subscription } from 'rxjs';
|
||||||
import { AuthService } from 'src/app/shared/auth/auth.service';
|
import { AuthService } from 'src/app/shared/service/auth.service';
|
||||||
import { SliderItemComponent } from 'src/app/shared/components/slider-item/slider-item.component';
|
import { SliderItemComponent } from 'src/app/shared/components/slider-item/slider-item.component';
|
||||||
import UserChecker from 'src/app/shared/model/user/user.checker';
|
import UserChecker from 'src/app/shared/model/user/user.checker';
|
||||||
import { User } from 'src/app/shared/model/user/user.model';
|
import { User } from 'src/app/shared/model/user/user.model';
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import {
|
|||||||
import { faUser } from '@fortawesome/free-solid-svg-icons';
|
import { faUser } from '@fortawesome/free-solid-svg-icons';
|
||||||
import { LoginComponent } from './header-popup/login/login.component';
|
import { LoginComponent } from './header-popup/login/login.component';
|
||||||
import { SignupComponent } from './header-popup/signup/signup.component';
|
import { SignupComponent } from './header-popup/signup/signup.component';
|
||||||
import { AuthService } from '../shared/auth/auth.service';
|
import { AuthService } from '../shared/service/auth.service';
|
||||||
import UserChecker from '../shared/model/user/user.checker';
|
import UserChecker from '../shared/model/user/user.checker';
|
||||||
import { User } from '../shared/model/user/user.model';
|
import { User } from '../shared/model/user/user.model';
|
||||||
import { Subscription } from 'rxjs';
|
import { Subscription } from 'rxjs';
|
||||||
@@ -25,7 +25,7 @@ import { MyProfileComponent } from './header-popup/my-profile/my-profile.compone
|
|||||||
export class HeaderComponent implements OnInit, OnDestroy {
|
export class HeaderComponent implements OnInit, OnDestroy {
|
||||||
pages: { name: string; route: string }[] = [
|
pages: { name: string; route: string }[] = [
|
||||||
{ name: 'Home', route: '/home' },
|
{ name: 'Home', route: '/home' },
|
||||||
{ name: 'Projects', route: '/home' },
|
{ name: 'Projects', route: '/projects' },
|
||||||
{ name: 'Contact', route: '/home' },
|
{ name: 'Contact', route: '/home' },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
18
src/app/home/home-router.module.ts
Normal file
18
src/app/home/home-router.module.ts
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { RouterModule, Routes } from '@angular/router';
|
||||||
|
import {HomeComponent} from "./home.component";
|
||||||
|
|
||||||
|
const routes: Routes = [
|
||||||
|
{
|
||||||
|
path: '',
|
||||||
|
component: HomeComponent,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [],
|
||||||
|
imports: [CommonModule, RouterModule.forChild(routes)],
|
||||||
|
exports: [RouterModule],
|
||||||
|
})
|
||||||
|
export class HomeRouterModule {}
|
||||||
@@ -4,6 +4,7 @@ import {HomeComponent} from "./home.component";
|
|||||||
import {StackSliderComponent} from "./stack-slider/stack-slider.component";
|
import {StackSliderComponent} from "./stack-slider/stack-slider.component";
|
||||||
import {StackCardComponent} from "./stack-slider/stack-card/stack-card.component";
|
import {StackCardComponent} from "./stack-slider/stack-card/stack-card.component";
|
||||||
import {NgxGlideComponent} from "ngx-glide";
|
import {NgxGlideComponent} from "ngx-glide";
|
||||||
|
import {HomeRouterModule} from "./home-router.module";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -16,7 +17,8 @@ import {NgxGlideComponent} from "ngx-glide";
|
|||||||
imports: [
|
imports: [
|
||||||
CommonModule,
|
CommonModule,
|
||||||
NgxGlideComponent,
|
NgxGlideComponent,
|
||||||
NgOptimizedImage
|
NgOptimizedImage,
|
||||||
|
HomeRouterModule
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
export class HomeModule { }
|
export class HomeModule { }
|
||||||
|
|||||||
@@ -14,6 +14,10 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
.stack-card-image::selection,
|
||||||
|
.stack-card-image *::selection {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
.stack-card-body {
|
.stack-card-body {
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
|
|||||||
94
src/app/projects/project-card/project-card.component.css
Normal file
94
src/app/projects/project-card/project-card.component.css
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
.card .inverse-card-image {
|
||||||
|
flex-direction: row-reverse;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card .card-content {
|
||||||
|
width: 100%;
|
||||||
|
padding: 40px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-around;
|
||||||
|
}
|
||||||
|
.card .card-content > * {
|
||||||
|
margin: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card .card-content .card-content-h {
|
||||||
|
width: 80%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card .card-content .card-title a {
|
||||||
|
font-size: 2rem;
|
||||||
|
font-weight: 600;
|
||||||
|
justify-content: left;
|
||||||
|
color: #767676;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card .card-content .card-text {
|
||||||
|
font-size: 1.2rem;
|
||||||
|
font-weight: 400;
|
||||||
|
color: #919294;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.card .card-content .card-content-f {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
flex-direction: row;
|
||||||
|
}
|
||||||
|
.card .card-content .card-content-f .card-stats {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: space-around;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.stat-item {
|
||||||
|
margin: 5px 0px 5px 0px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: start;
|
||||||
|
align-content: start;
|
||||||
|
|
||||||
|
color: #919294;
|
||||||
|
}
|
||||||
|
.stat-item .stat-icon {
|
||||||
|
margin: 0px 15px 0px 0px;
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
font-size: 1rem;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
.stat-item span {
|
||||||
|
font-size: 1rem;
|
||||||
|
font-weight: 300;
|
||||||
|
margin: 0;
|
||||||
|
justify-content: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stats-inline {
|
||||||
|
margin: 0;
|
||||||
|
width: 100%;
|
||||||
|
flex-direction: row !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-languages {
|
||||||
|
padding: 0;
|
||||||
|
margin-top: 25px;
|
||||||
|
margin-bottom: 25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* COMPUTER FORMAT */
|
||||||
|
@media only screen and (min-width: 770px) {
|
||||||
|
.card-languages {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card .card-content .card-content-f .card-stats {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-around;
|
||||||
|
}
|
||||||
|
}
|
||||||
49
src/app/projects/project-card/project-card.component.html
Normal file
49
src/app/projects/project-card/project-card.component.html
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
<div class="card container">
|
||||||
|
<div class="card-content">
|
||||||
|
<div class="card-content-h">
|
||||||
|
<h2 class="card-title">
|
||||||
|
<a [href]="project.link">{{project.name}}</a>
|
||||||
|
</h2>
|
||||||
|
<p class="card-text">{{project.description}}</p>
|
||||||
|
</div>
|
||||||
|
<div class="card-content-f row">
|
||||||
|
<div class="card-languages col-md-9" *ngIf="hasLanguage" id="language-chart">
|
||||||
|
<apx-chart *ngIf="chartOptions !== undefined"
|
||||||
|
[series]="chartOptions.series"
|
||||||
|
[colors]="chartOptions.colors"
|
||||||
|
[chart]="chartOptions.chart"
|
||||||
|
[labels]="chartOptions.labels"
|
||||||
|
[responsive]="chartOptions.responsive"
|
||||||
|
[plotOptions]="chartOptions.plotOptions"
|
||||||
|
[dataLabels]="chartOptions.dataLabels">
|
||||||
|
</apx-chart>
|
||||||
|
</div>
|
||||||
|
<div class="card-stats" [ngClass]="hasLanguage ? 'col-md-3' : 'stats-inline'">
|
||||||
|
<div class="stat-item" *ngIf="hasLicense">
|
||||||
|
<div class="stat-icon">
|
||||||
|
<fa-icon [icon]="faLicense"></fa-icon>
|
||||||
|
</div>
|
||||||
|
<span>{{project.license}}</span>
|
||||||
|
</div>
|
||||||
|
<div class="stat-item">
|
||||||
|
<div class="stat-icon">
|
||||||
|
<fa-icon [icon]="faStars"></fa-icon>
|
||||||
|
</div>
|
||||||
|
<span>{{project.stars}}</span>
|
||||||
|
</div>
|
||||||
|
<div class="stat-item">
|
||||||
|
<div class="stat-icon">
|
||||||
|
<fa-icon [icon]="faCodeFork"></fa-icon>
|
||||||
|
</div>
|
||||||
|
<span>{{project.forks}}</span>
|
||||||
|
</div>
|
||||||
|
<div class="stat-item">
|
||||||
|
<div class="stat-icon">
|
||||||
|
<fa-icon [icon]="faEye"></fa-icon>
|
||||||
|
</div>
|
||||||
|
<span>{{project.watchers}}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
114
src/app/projects/project-card/project-card.component.ts
Normal file
114
src/app/projects/project-card/project-card.component.ts
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
import {Component, HostListener, Input, OnInit, ViewChild} from '@angular/core';
|
||||||
|
import {faCodeFork, faEye, faScaleBalanced, faStar} from '@fortawesome/free-solid-svg-icons';
|
||||||
|
import {Language, Project} from "../../shared/model/project/project.model";
|
||||||
|
import {
|
||||||
|
ApexChart, ApexDataLabels,
|
||||||
|
ApexNonAxisChartSeries,
|
||||||
|
ApexPlotOptions,
|
||||||
|
ApexResponsive,
|
||||||
|
ChartComponent
|
||||||
|
} from "ng-apexcharts";
|
||||||
|
|
||||||
|
|
||||||
|
export type ChartOptions = {
|
||||||
|
series: ApexNonAxisChartSeries;
|
||||||
|
colors: string[];
|
||||||
|
chart: ApexChart;
|
||||||
|
responsive: ApexResponsive[];
|
||||||
|
labels: string[];
|
||||||
|
plotOptions: ApexPlotOptions;
|
||||||
|
dataLabels: ApexDataLabels;
|
||||||
|
};
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-project-card',
|
||||||
|
templateUrl: './project-card.component.html',
|
||||||
|
styleUrls: ['./project-card.component.css']
|
||||||
|
})
|
||||||
|
export class ProjectCardComponent implements OnInit {
|
||||||
|
@Input() inverted: boolean = false;
|
||||||
|
|
||||||
|
@Input() project!: Project;
|
||||||
|
|
||||||
|
@ViewChild('language-chart')
|
||||||
|
languageChart: ChartComponent | undefined;
|
||||||
|
|
||||||
|
chartOptions: ChartOptions | undefined;
|
||||||
|
|
||||||
|
// Stats Icons Definitions
|
||||||
|
faLicense = faScaleBalanced;
|
||||||
|
|
||||||
|
faStars = faStar;
|
||||||
|
|
||||||
|
faCodeFork = faCodeFork;
|
||||||
|
|
||||||
|
faEye = faEye;
|
||||||
|
|
||||||
|
private windowResizeTimeout: any;
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
if (!!this.project.languages) {
|
||||||
|
const windowWidth = window.innerWidth;
|
||||||
|
this.chartOptions = this.generateChart(this.project.languages, windowWidth);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@HostListener('window:resize', ['$event'])
|
||||||
|
getScreenSize(event: Event) {
|
||||||
|
clearTimeout(this.windowResizeTimeout);
|
||||||
|
|
||||||
|
this.windowResizeTimeout = setTimeout(() => {
|
||||||
|
if (!this.project.languages) return;
|
||||||
|
this.chartOptions = this.generateChart(
|
||||||
|
this.project.languages, window.innerWidth
|
||||||
|
);
|
||||||
|
}, 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
get hasLicense(): boolean {
|
||||||
|
return this.project.license !== undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
get hasLanguage(): boolean {
|
||||||
|
return this.project.languages !== undefined &&
|
||||||
|
this.project.languages?.length > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private generateChart(languages: Language[], windowWidth: number): ChartOptions {
|
||||||
|
const responsiveWindowWidth = windowWidth >= 530 ?
|
||||||
|
300 : (windowWidth*.8 - 80);
|
||||||
|
|
||||||
|
return {
|
||||||
|
series: languages.map(value => value.percentage),
|
||||||
|
colors: languages.map(value => value.color),
|
||||||
|
chart: {
|
||||||
|
width: 380,
|
||||||
|
type: "donut"
|
||||||
|
},
|
||||||
|
labels: languages.map(value => value.name),
|
||||||
|
responsive: [
|
||||||
|
{
|
||||||
|
breakpoint: 530,
|
||||||
|
options: {
|
||||||
|
chart: {
|
||||||
|
width: responsiveWindowWidth
|
||||||
|
},
|
||||||
|
legend: {
|
||||||
|
position: "bottom"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
plotOptions: {
|
||||||
|
pie: {
|
||||||
|
expandOnClick: true,
|
||||||
|
|
||||||
|
}
|
||||||
|
},
|
||||||
|
dataLabels: {
|
||||||
|
enabled: false
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
18
src/app/projects/projects-router.module.ts
Normal file
18
src/app/projects/projects-router.module.ts
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { RouterModule, Routes } from '@angular/router';
|
||||||
|
import {ProjectsComponent} from "./projects.component";
|
||||||
|
|
||||||
|
const routes: Routes = [
|
||||||
|
{
|
||||||
|
path: '',
|
||||||
|
component: ProjectsComponent
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [],
|
||||||
|
imports: [CommonModule, RouterModule.forChild(routes)],
|
||||||
|
exports: [RouterModule],
|
||||||
|
})
|
||||||
|
export class ProjectsRouterModule {}
|
||||||
8
src/app/projects/projects.component.css
Normal file
8
src/app/projects/projects.component.css
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
app-project-card {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
margin-top: 25px;
|
||||||
|
}
|
||||||
|
app-project-card:last-child {
|
||||||
|
margin-bottom: 25px;
|
||||||
|
}
|
||||||
6
src/app/projects/projects.component.html
Normal file
6
src/app/projects/projects.component.html
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<div class="container">
|
||||||
|
<div *ngFor="let p of projects; index as i;trackBy: identifyProject">
|
||||||
|
<app-project-card [project]="p" [inverted]="i % 2 !== 0">
|
||||||
|
</app-project-card>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
27
src/app/projects/projects.component.ts
Normal file
27
src/app/projects/projects.component.ts
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
import {Component, OnInit} from '@angular/core';
|
||||||
|
import {GithubService} from "../shared/service/github.service";
|
||||||
|
import {Project} from "../shared/model/project/project.model";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-projects',
|
||||||
|
templateUrl: './projects.component.html',
|
||||||
|
styleUrls: ['./projects.component.css']
|
||||||
|
})
|
||||||
|
export class ProjectsComponent implements OnInit {
|
||||||
|
projects!: Project[];
|
||||||
|
|
||||||
|
constructor(private githubService: GithubService) {
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.projects = [];
|
||||||
|
this.githubService.getProjects().subscribe((project: Project) => {
|
||||||
|
this.projects.push(project);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
identifyProject(index: number, project: Project) {
|
||||||
|
return project.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
25
src/app/projects/projects.module.ts
Normal file
25
src/app/projects/projects.module.ts
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import {NgModule} from "@angular/core";
|
||||||
|
import {ProjectsComponent} from "./projects.component";
|
||||||
|
import {CommonModule, NgOptimizedImage} from "@angular/common";
|
||||||
|
import { ProjectCardComponent } from './project-card/project-card.component';
|
||||||
|
import {MatIconModule} from "@angular/material/icon";
|
||||||
|
import {FontAwesomeModule} from "@fortawesome/angular-fontawesome";
|
||||||
|
import {NgApexchartsModule} from "ng-apexcharts";
|
||||||
|
import {ProjectsRouterModule} from "./projects-router.module";
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [
|
||||||
|
ProjectsComponent,
|
||||||
|
ProjectCardComponent
|
||||||
|
],
|
||||||
|
imports: [
|
||||||
|
CommonModule,
|
||||||
|
NgOptimizedImage,
|
||||||
|
MatIconModule,
|
||||||
|
FontAwesomeModule,
|
||||||
|
NgApexchartsModule,
|
||||||
|
ProjectsRouterModule
|
||||||
|
],
|
||||||
|
exports: []
|
||||||
|
})
|
||||||
|
export class ProjectsModule { }
|
||||||
18
src/app/shared/model/project/project.model.ts
Normal file
18
src/app/shared/model/project/project.model.ts
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
export type Language = {
|
||||||
|
name: string;
|
||||||
|
color: string;
|
||||||
|
percentage: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Project = {
|
||||||
|
name: string;
|
||||||
|
description: string;
|
||||||
|
link: string;
|
||||||
|
|
||||||
|
license?: string;
|
||||||
|
languages?: Language[];
|
||||||
|
|
||||||
|
stars: number;
|
||||||
|
forks: number;
|
||||||
|
watchers: number;
|
||||||
|
}
|
||||||
@@ -2,19 +2,15 @@ import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http';
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import {
|
import {
|
||||||
first,
|
first,
|
||||||
firstValueFrom,
|
|
||||||
map,
|
map,
|
||||||
Observable,
|
Observable,
|
||||||
of,
|
of,
|
||||||
Subject,
|
Subject,
|
||||||
take,
|
|
||||||
tap,
|
|
||||||
} from 'rxjs';
|
} from 'rxjs';
|
||||||
import { catchError } from 'rxjs/operators';
|
import { catchError } from 'rxjs/operators';
|
||||||
import { environment } from 'src/environments/environment';
|
import { environment } from 'src/environments/environment';
|
||||||
import { HttpError } from '../model/httpError/httpError.model';
|
import { HttpError } from '../model/httpError/httpError.model';
|
||||||
import { User } from '../model/user/user.model';
|
import { User } from '../model/user/user.model';
|
||||||
import * as http from 'http';
|
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root',
|
providedIn: 'root',
|
||||||
155
src/app/shared/service/github.service.ts
Normal file
155
src/app/shared/service/github.service.ts
Normal file
@@ -0,0 +1,155 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import {Language, Project} from "../model/project/project.model";
|
||||||
|
import {HttpClient} from "@angular/common/http";
|
||||||
|
import {
|
||||||
|
map, mergeMap,
|
||||||
|
Observable,
|
||||||
|
pipe, switchMap, take, tap
|
||||||
|
} from 'rxjs';
|
||||||
|
import { environment } from 'src/environments/environment';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class GithubService {
|
||||||
|
|
||||||
|
GITHUB_API_URL = 'https://api.github.com';
|
||||||
|
|
||||||
|
GITHUB_API_COLORS = 'https://raw.githubusercontent.com/ozh/github-colors/master/colors.json';
|
||||||
|
|
||||||
|
GITHUB_USER = environment.githubUser;
|
||||||
|
|
||||||
|
colors!: Map<string, string>;
|
||||||
|
|
||||||
|
constructor(private http: HttpClient) {
|
||||||
|
this.getLanguageColor().subscribe((colors: Map<string, string>) => {
|
||||||
|
this.colors = colors;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getProjects(): Observable<Project> {
|
||||||
|
if (this.isLocalStorageValid()) {
|
||||||
|
console.log('Fetching projects from local storage')
|
||||||
|
return this.getProjectsFromLocalStorage();
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('Fetching projects from Github API')
|
||||||
|
return this.getProjectsFromGithub()
|
||||||
|
}
|
||||||
|
|
||||||
|
private isLocalStorageValid(): boolean {
|
||||||
|
const projects = localStorage.getItem('github-projects');
|
||||||
|
let status = !!projects
|
||||||
|
|
||||||
|
if (!status) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const timestamp = localStorage.getItem('github-projects-timestamp');
|
||||||
|
if (timestamp) {
|
||||||
|
const diff = new Date().getTime() - parseInt(timestamp);
|
||||||
|
status = diff < 86400000;
|
||||||
|
} else {
|
||||||
|
status = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
private saveProjectsToLocalStorage(project: Project) {
|
||||||
|
const p = localStorage.getItem('github-projects');
|
||||||
|
if (p) {
|
||||||
|
const projects = JSON.parse(p);
|
||||||
|
projects.push(project);
|
||||||
|
localStorage.setItem('github-projects', JSON.stringify(projects));
|
||||||
|
} else {
|
||||||
|
localStorage.setItem('github-projects', JSON.stringify([project]));
|
||||||
|
}
|
||||||
|
|
||||||
|
localStorage.setItem('github-projects-timestamp', new Date().getTime().toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
private getProjectsFromLocalStorage(): Observable<Project> {
|
||||||
|
const projects = localStorage.getItem('github-projects') || '[]';
|
||||||
|
return new Observable<Project>((observer) => {
|
||||||
|
JSON.parse(projects).forEach((project: Project) => {
|
||||||
|
observer.next(project);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private getProjectsFromGithub(): Observable<Project> {
|
||||||
|
return this.http.get(this.apiReposString()).pipe(
|
||||||
|
map((projects: any) => {
|
||||||
|
return projects.map((project: any) => {
|
||||||
|
return {
|
||||||
|
name: project.name,
|
||||||
|
description: project.description,
|
||||||
|
license: project.license?.key,
|
||||||
|
link: project.html_url,
|
||||||
|
|
||||||
|
stars: project.stargazers_count,
|
||||||
|
forks: project.forks_count,
|
||||||
|
watchers: project.watchers_count
|
||||||
|
} as Project;
|
||||||
|
}).filter((project: Project) => {
|
||||||
|
return project.name !== this.GITHUB_USER;
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
switchMap((projects: Project[]) => {
|
||||||
|
return new Observable<Project>((observer) => {
|
||||||
|
projects.forEach((project: Project, index: number) => {
|
||||||
|
this.getProjectLanguage(project).subscribe((languages: Language[]) => {
|
||||||
|
project.languages = languages;
|
||||||
|
observer.next(project);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
tap((project: Project) => this.saveProjectsToLocalStorage(project))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private getProjectLanguage(project: Project): Observable<Language[]> {
|
||||||
|
return this.http.get(this.apiRepoLanguagesString(project.name)).pipe(
|
||||||
|
map((languages: any) => {
|
||||||
|
let totalBytes = 0;
|
||||||
|
Object.keys(languages).forEach((language: string) => {
|
||||||
|
totalBytes += languages[language];
|
||||||
|
});
|
||||||
|
|
||||||
|
return Object.keys(languages).map((language: string) => {
|
||||||
|
return {
|
||||||
|
name: language,
|
||||||
|
color: this.colors.get(language) || this.getRandColor(),
|
||||||
|
percentage: (languages[language]/totalBytes)*100
|
||||||
|
} as Language;
|
||||||
|
});
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private getLanguageColor(): Observable<Map<string, string>> {
|
||||||
|
return this.http.get(this.GITHUB_API_COLORS).pipe(
|
||||||
|
map((colors: any) => {
|
||||||
|
const colorMap = new Map<string, string>();
|
||||||
|
Object.keys(colors).forEach((language: string) => {
|
||||||
|
colorMap.set(language, colors[language].color);
|
||||||
|
});
|
||||||
|
return colorMap;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private getRandColor(): string {
|
||||||
|
return `#${Math.floor(Math.random()*16777215).toString(16)}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private apiReposString() {
|
||||||
|
return `${this.GITHUB_API_URL}/users/${this.GITHUB_USER}/repos`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private apiRepoLanguagesString(repoName: string) {
|
||||||
|
return `${this.GITHUB_API_URL}/repos/${this.GITHUB_USER}/${repoName}/languages`;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,4 +4,5 @@
|
|||||||
// Environment variables
|
// Environment variables
|
||||||
window["env"]["BACKEND_URL"] = "${BACKEND_URL}";
|
window["env"]["BACKEND_URL"] = "${BACKEND_URL}";
|
||||||
window["env"]["BACKEND_OAUTH_URL"] = "${BACKEND_OAUTH_URL}";
|
window["env"]["BACKEND_OAUTH_URL"] = "${BACKEND_OAUTH_URL}";
|
||||||
|
window["env"]["GITHUB_USER"] = "${GITHUB_USER}";
|
||||||
})(this);
|
})(this);
|
||||||
|
|||||||
@@ -2,4 +2,5 @@ export const environment = {
|
|||||||
production: true,
|
production: true,
|
||||||
backendPath: (<any>window)['env']['BACKEND_URL'],
|
backendPath: (<any>window)['env']['BACKEND_URL'],
|
||||||
backendOAuthPath: (<any>window)['env']['BACKEND_OAUTH_URL'],
|
backendOAuthPath: (<any>window)['env']['BACKEND_OAUTH_URL'],
|
||||||
|
githubUser: (<any>window)['env']['GITHUB_USER'],
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ export const environment = {
|
|||||||
production: false,
|
production: false,
|
||||||
backendPath: 'http://localhost:8070',
|
backendPath: 'http://localhost:8070',
|
||||||
backendOAuthPath: 'http://localhost:8070',
|
backendOAuthPath: 'http://localhost:8070',
|
||||||
|
githubUser: 'HideyoshiNakazone',
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
Reference in New Issue
Block a user