├── .editorconfig
├── .github
└── ISSUE_TEMPLATE
│ ├── bug_report.md
│ ├── custom.md
│ ├── feature_request.md
│ └── stale.yaml
├── .gitignore
├── .htmlhintrc
├── .stylelintrc
├── .yo-rc.json
├── CONTRIBUTE.md
├── LICENSE
├── README.md
├── angular.json
├── app.yaml
├── apple-touch-icon.png
├── deploy.sh
├── docs
├── analytics.md
├── backend-proxy.md
├── coding-guides
│ ├── angular.md
│ ├── e2e-tests.md
│ ├── html.md
│ ├── sass.md
│ ├── typescript.md
│ └── unit-tests.md
├── corporate-proxy.md
├── i18n.md
├── readme.md
├── routing.md
└── updating.md
├── e2e
├── protractor.conf.js
├── src
│ ├── app.e2e-spec.ts
│ └── app.po.ts
└── tsconfig.e2e.json
├── ngsw-config.json
├── package.json
├── proxy.conf.json
├── public-schema-edited.sql
├── public-schema.sql
├── public-schema1.sql
├── src
├── _redirects
├── app
│ ├── add
│ │ ├── add-project
│ │ │ ├── add-project.component.html
│ │ │ ├── add-project.component.scss
│ │ │ ├── add-project.component.spec.ts
│ │ │ └── add-project.component.ts
│ │ ├── add-routing.module.ts
│ │ ├── add.module.ts
│ │ └── mentor
│ │ │ ├── mentor.component.html
│ │ │ ├── mentor.component.scss
│ │ │ ├── mentor.component.spec.ts
│ │ │ └── mentor.component.ts
│ ├── animations
│ │ └── router.animations.ts
│ ├── app-routing.module.ts
│ ├── app.component.html
│ ├── app.component.scss
│ ├── app.component.spec.ts
│ ├── app.component.ts
│ ├── app.module.ts
│ ├── auth
│ │ ├── auth-routing.module.ts
│ │ ├── auth.module.ts
│ │ └── login
│ │ │ ├── login.component.html
│ │ │ ├── login.component.scss
│ │ │ ├── login.component.spec.ts
│ │ │ └── login.component.ts
│ ├── core
│ │ ├── authentication
│ │ │ ├── authentication.guard.ts
│ │ │ └── authentication.service.ts
│ │ ├── comments
│ │ │ ├── comments.service.spec.ts
│ │ │ └── comments.service.ts
│ │ ├── core.module.ts
│ │ ├── error-handler.service.spec.ts
│ │ ├── error-handler.service.ts
│ │ ├── http
│ │ │ ├── api-prefix.interceptor.spec.ts
│ │ │ ├── api-prefix.interceptor.ts
│ │ │ ├── cache.interceptor.spec.ts
│ │ │ ├── cache.interceptor.ts
│ │ │ ├── error-handler.interceptor.spec.ts
│ │ │ ├── error-handler.interceptor.ts
│ │ │ ├── http-cache.service.spec.ts
│ │ │ ├── http-cache.service.ts
│ │ │ ├── http.service.spec.ts
│ │ │ └── http.service.ts
│ │ ├── index.ts
│ │ ├── issue
│ │ │ ├── issue.service.spec.ts
│ │ │ └── issue.service.ts
│ │ ├── like
│ │ │ ├── like.service.spec.ts
│ │ │ └── like.service.ts
│ │ ├── logger.service.spec.ts
│ │ ├── logger.service.ts
│ │ ├── profile
│ │ │ ├── profile.service.spec.ts
│ │ │ ├── profile.service.ts
│ │ │ └── user.ts
│ │ ├── project
│ │ │ ├── project.service.spec.ts
│ │ │ └── project.service.ts
│ │ ├── review
│ │ │ ├── review.service.spec.ts
│ │ │ └── review.service.ts
│ │ ├── route-reusable-strategy.ts
│ │ ├── tags
│ │ │ ├── tags.service.spec.ts
│ │ │ └── tags.service.ts
│ │ └── user
│ │ │ ├── user.service.spec.ts
│ │ │ └── user.service.ts
│ ├── home
│ │ ├── home-routing.module.ts
│ │ ├── home.component.html
│ │ ├── home.component.scss
│ │ ├── home.component.spec.ts
│ │ ├── home.component.ts
│ │ └── home.module.ts
│ ├── material.module.ts
│ ├── profile
│ │ ├── profile-routing.module.ts
│ │ ├── profile.module.ts
│ │ └── user
│ │ │ ├── projects
│ │ │ ├── projects.component.html
│ │ │ ├── projects.component.scss
│ │ │ ├── projects.component.spec.ts
│ │ │ └── projects.component.ts
│ │ │ ├── user-routing.module.ts
│ │ │ ├── user.component.html
│ │ │ ├── user.component.scss
│ │ │ ├── user.component.spec.ts
│ │ │ ├── user.component.ts
│ │ │ └── user.module.ts
│ ├── shared
│ │ ├── activity-actions
│ │ │ ├── activity-actions.component.html
│ │ │ ├── activity-actions.component.scss
│ │ │ ├── activity-actions.component.spec.ts
│ │ │ └── activity-actions.component.ts
│ │ ├── comments
│ │ │ ├── comments.component.html
│ │ │ ├── comments.component.scss
│ │ │ ├── comments.component.spec.ts
│ │ │ ├── comments.component.ts
│ │ │ └── editable-comment
│ │ │ │ ├── editable-comment.component.html
│ │ │ │ ├── editable-comment.component.scss
│ │ │ │ ├── editable-comment.component.spec.ts
│ │ │ │ └── editable-comment.component.ts
│ │ ├── constants.ts
│ │ ├── fragments
│ │ │ ├── project-fragments.ts
│ │ │ └── user-fragments.ts
│ │ ├── index.ts
│ │ ├── like
│ │ │ ├── like.component.html
│ │ │ ├── like.component.scss
│ │ │ ├── like.component.spec.ts
│ │ │ └── like.component.ts
│ │ ├── mutations
│ │ │ ├── project-mutations.ts
│ │ │ ├── review-mutation.ts
│ │ │ ├── tags-mutations.ts
│ │ │ └── user-mutations.ts
│ │ ├── not-found
│ │ │ └── not-found.component.ts
│ │ ├── objects.ts
│ │ ├── pipes
│ │ │ ├── time-diff.pipe.spec.ts
│ │ │ └── time-diff.pipe.ts
│ │ ├── queries
│ │ │ ├── project-queries.ts
│ │ │ ├── review-queries.ts
│ │ │ └── user-queries.ts
│ │ ├── share-sheet
│ │ │ ├── share-sheet.component.html
│ │ │ ├── share-sheet.component.scss
│ │ │ ├── share-sheet.component.spec.ts
│ │ │ └── share-sheet.component.ts
│ │ ├── shared.module.ts
│ │ ├── unauthorized
│ │ │ └── unauthorized.component.ts
│ │ └── user-name
│ │ │ ├── user-name.component.html
│ │ │ ├── user-name.component.scss
│ │ │ ├── user-name.component.spec.ts
│ │ │ └── user-name.component.ts
│ ├── shell
│ │ ├── graphql
│ │ │ └── graphql.module.ts
│ │ ├── header
│ │ │ ├── header.component.html
│ │ │ ├── header.component.scss
│ │ │ ├── header.component.spec.ts
│ │ │ ├── header.component.ts
│ │ │ └── requests
│ │ │ │ ├── requests.component.html
│ │ │ │ ├── requests.component.scss
│ │ │ │ └── requests.component.ts
│ │ ├── shell.component.html
│ │ ├── shell.component.scss
│ │ ├── shell.component.spec.ts
│ │ ├── shell.component.ts
│ │ ├── shell.module.spec.ts
│ │ ├── shell.module.ts
│ │ ├── shell.service.spec.ts
│ │ └── shell.service.ts
│ └── view
│ │ ├── feed
│ │ ├── feed-project
│ │ │ ├── feed-project.component.html
│ │ │ ├── feed-project.component.scss
│ │ │ ├── feed-project.component.spec.ts
│ │ │ └── feed-project.component.ts
│ │ ├── feed-routing.module.ts
│ │ ├── feed.module.ts
│ │ ├── issues-feed
│ │ │ ├── issues-feed.component.html
│ │ │ ├── issues-feed.component.scss
│ │ │ ├── issues-feed.component.spec.ts
│ │ │ └── issues-feed.component.ts
│ │ └── launched
│ │ │ ├── launched-products
│ │ │ ├── launched-products.component.html
│ │ │ ├── launched-products.component.scss
│ │ │ ├── launched-products.component.spec.ts
│ │ │ └── launched-products.component.ts
│ │ │ ├── launched-routing.module.ts
│ │ │ └── launched.module.ts
│ │ ├── issues
│ │ ├── issue-card
│ │ │ ├── issue-card.component.html
│ │ │ ├── issue-card.component.scss
│ │ │ ├── issue-card.component.spec.ts
│ │ │ └── issue-card.component.ts
│ │ ├── issue-feed.component.html
│ │ ├── issue-feed.component.scss
│ │ ├── issue-feed.component.spec.ts
│ │ ├── issue-feed.component.ts
│ │ ├── issues-routing.module.ts
│ │ └── issues.module.ts
│ │ ├── review-answers
│ │ ├── checkpoint-tab
│ │ │ ├── action-modal
│ │ │ │ ├── action-modal.component.html
│ │ │ │ ├── action-modal.component.scss
│ │ │ │ ├── action-modal.component.spec.ts
│ │ │ │ └── action-modal.component.ts
│ │ │ ├── checkpoint-tab.component.html
│ │ │ ├── checkpoint-tab.component.scss
│ │ │ ├── checkpoint-tab.component.spec.ts
│ │ │ └── checkpoint-tab.component.ts
│ │ ├── review-answers-routing.module.ts
│ │ ├── review-answers.component.html
│ │ ├── review-answers.component.scss
│ │ ├── review-answers.component.spec.ts
│ │ ├── review-answers.component.ts
│ │ └── review-answers.module.ts
│ │ ├── view-project
│ │ ├── issue
│ │ │ ├── add-issue.html
│ │ │ ├── issue.component.html
│ │ │ ├── issue.component.scss
│ │ │ ├── issue.component.spec.ts
│ │ │ └── issue.component.ts
│ │ ├── project-home
│ │ │ ├── project-home.component.html
│ │ │ ├── project-home.component.scss
│ │ │ ├── project-home.component.spec.ts
│ │ │ ├── project-home.component.ts
│ │ │ └── review-responses
│ │ │ │ ├── review-responses.component.html
│ │ │ │ ├── review-responses.component.scss
│ │ │ │ ├── review-responses.component.spec.ts
│ │ │ │ └── review-responses.component.ts
│ │ ├── project-progress
│ │ │ ├── project-progress.component.html
│ │ │ ├── project-progress.component.scss
│ │ │ ├── project-progress.component.spec.ts
│ │ │ └── project-progress.component.ts
│ │ ├── project-timeline
│ │ │ ├── project-timeline.component.html
│ │ │ ├── project-timeline.component.scss
│ │ │ ├── project-timeline.component.spec.ts
│ │ │ └── project-timeline.component.ts
│ │ ├── review
│ │ │ ├── review.component.html
│ │ │ ├── review.component.scss
│ │ │ ├── review.component.spec.ts
│ │ │ └── review.component.ts
│ │ ├── view-project-routing.module.ts
│ │ ├── view-project.component.html
│ │ ├── view-project.component.scss
│ │ ├── view-project.component.spec.ts
│ │ ├── view-project.component.ts
│ │ └── view-project.module.ts
│ │ ├── view-routing.module.ts
│ │ └── view.module.ts
├── assets
│ ├── add-idea.svg
│ ├── cynthesize-banner-logo.png
│ ├── cynthesize-logo.png
│ ├── fonts
│ │ ├── Gilroy-Black.ttf
│ │ ├── Gilroy-BlackItalic.ttf
│ │ ├── Gilroy-Bold.ttf
│ │ ├── Gilroy-BoldItalic.ttf
│ │ ├── Gilroy-ExtraBold.ttf
│ │ ├── Gilroy-ExtraBoldItalic.ttf
│ │ ├── Gilroy-Heavy.ttf
│ │ ├── Gilroy-HeavyItalic.ttf
│ │ ├── Gilroy-Light.ttf
│ │ ├── Gilroy-LightItalic.ttf
│ │ ├── Gilroy-Medium.ttf
│ │ ├── Gilroy-MediumItalic.ttf
│ │ ├── Gilroy-Regular.ttf
│ │ ├── Gilroy-RegularItalic.ttf
│ │ ├── Gilroy-SemiBold.ttf
│ │ ├── Gilroy-SemiBoldItalic.ttf
│ │ ├── Gilroy-Thin.ttf
│ │ ├── Gilroy-ThinItalic.ttf
│ │ ├── Gilroy-UltraLight.ttf
│ │ └── Gilroy-UltraLightItalic.ttf
│ ├── home.svg
│ ├── images
│ │ ├── add-issue.svg
│ │ ├── become-mentor.svg
│ │ ├── briefing-story.svg
│ │ ├── briefing.svg
│ │ ├── building.svg
│ │ ├── design.svg
│ │ ├── full.png
│ │ ├── home_bg_1.svg
│ │ ├── idea-selected.svg
│ │ ├── idea.svg
│ │ ├── ideas.png
│ │ ├── none_found.svg
│ │ ├── profile-selected.svg
│ │ ├── profile.svg
│ │ ├── project.png
│ │ ├── project_banner.svg
│ │ ├── pw_maze_black.png
│ │ └── waiting.svg
│ ├── intersection.svg
│ ├── logos
│ │ ├── Asset 2MY.png
│ │ ├── Asset 3MY.png
│ │ ├── Asset 4MY.png
│ │ ├── profilePicture.jpg
│ │ └── profilePicture2.jpg
│ ├── not_found.svg
│ ├── stages
│ │ ├── consumer_feedback_stage.svg
│ │ ├── funding_stage.svg
│ │ ├── ideation_stage.svg
│ │ ├── launching_and_testing_stage.svg
│ │ ├── marketing_stage.svg
│ │ └── prototype_development_stage.svg
│ ├── unauthorised.svg
│ └── user.png
├── browserslist
├── environments
│ ├── environment.prod.ts
│ └── environment.ts
├── favicon.ico
├── index.html
├── karma.conf.js
├── main.scss
├── main.ts
├── manifest.json
├── polyfills.ts
├── robots.txt
├── sass
│ ├── _declarations.scss
│ ├── _mixins.scss
│ ├── _rules.scss
│ ├── _typography.scss
│ ├── _variables.scss
│ └── components
│ │ └── _login-register.scss
├── test.ts
├── theme
│ ├── theme-variables.scss
│ └── theme.scss
├── translations
│ ├── en-US.json
│ └── fr-FR.json
├── tsconfig.app.json
├── tsconfig.spec.json
└── typings.d.ts
├── tsconfig.json
└── tslint.json
/.editorconfig:
--------------------------------------------------------------------------------
1 | # Editor configuration, see http://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | charset = utf-8
6 | indent_style = space
7 | indent_size = 2
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 | end_of_line = lf
11 | max_line_length = 120
12 |
13 | [*.md]
14 | max_line_length = off
15 | trim_trailing_whitespace = false
16 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Describe the bug**
11 | A clear and concise description of what the bug is.
12 |
13 | **To Reproduce**
14 | Steps to reproduce the behavior:
15 | 1. Go to '...'
16 | 2. Click on '....'
17 | 3. Scroll down to '....'
18 | 4. See error
19 |
20 | **Expected behavior**
21 | A clear and concise description of what you expected to happen.
22 |
23 | **Screenshots**
24 | If applicable, add screenshots to help explain your problem.
25 |
26 | **Desktop (please complete the following information):**
27 | - OS: [e.g. iOS]
28 | - Browser [e.g. chrome, safari]
29 | - Version [e.g. 22]
30 |
31 | **Smartphone (please complete the following information):**
32 | - Device: [e.g. iPhone6]
33 | - OS: [e.g. iOS8.1]
34 | - Browser [e.g. stock browser, safari]
35 | - Version [e.g. 22]
36 |
37 | **Additional context**
38 | Add any other context about the problem here.
39 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/custom.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Custom issue template
3 | about: Describe this issue template's purpose here.
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 |
11 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Is your feature request related to a problem? Please describe.**
11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12 |
13 | **Describe the solution you'd like**
14 | A clear and concise description of what you want to happen.
15 |
16 | **Describe alternatives you've considered**
17 | A clear and concise description of any alternative solutions or features you've considered.
18 |
19 | **Additional context**
20 | Add any other context or screenshots about the feature request here.
21 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/stale.yaml:
--------------------------------------------------------------------------------
1 | # Number of days of inactivity before an issue becomes stale
2 | # daysUntilStale: 60
3 | # Number of days of inactivity before a stale issue is closed
4 | daysUntilClose: 7
5 | # Issues with these labels will never be considered stale
6 | exemptLabels:
7 | - pinned
8 | - security
9 | # Label to use when marking an issue as stale
10 | staleLabel: stale
11 | # Comment to post when marking an issue as stale. Set to `false` to disable
12 | markComment: >
13 | This issue has been automatically marked as stale because it has not had
14 | recent activity. It will be closed if no further activity occurs. Thank you
15 | for your contributions.
16 | # Comment to post when closing a stale issue. Set to `false` to disable
17 | closeComment: false
18 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See http://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # Compiled output
4 | /dist
5 | /tmp
6 | /out-tsc
7 |
8 | # Dependencies
9 | /node_modules
10 |
11 | # Cordova
12 | /www
13 | /plugins
14 | /platforms
15 |
16 | # IDEs and editors
17 | .idea/*
18 | !.idea/runConfigurations/
19 | !.idea/codeStyleSettings.xml
20 | .project
21 | .classpath
22 | .c9/
23 | *.launch
24 | .settings/
25 | xcuserdata/
26 | *.sublime-workspace
27 |
28 | # IDE - VSCode
29 | .vscode/*
30 | !.vscode/settings.json
31 | !.vscode/tasks.json
32 | !.vscode/launch.json
33 | !.vscode/extensions.json
34 | *.vscode
35 |
36 | # Maven
37 | /target
38 | /log
39 |
40 | # Misc
41 | /.sass-cache
42 | /connect.lock
43 | /package-lock.json
44 | /yarn.lock
45 | /coverage
46 | /libpeerconnection.log
47 | npm-debug.log
48 | yarn-error.log
49 | testem.log
50 | /typings
51 | /reports
52 | /src/translations/template.*
53 | /src/environments/.env.*
54 |
55 | # System Files
56 | .DS_Store
57 | Thumbs.db
58 |
59 | proxy.conf.json
60 |
--------------------------------------------------------------------------------
/.htmlhintrc:
--------------------------------------------------------------------------------
1 | {
2 | "tagname-lowercase": false,
3 | "attr-lowercase": false,
4 | "attr-value-double-quotes": true,
5 | "tag-pair": true,
6 | "spec-char-escape": true,
7 | "id-unique": true,
8 | "src-not-empty": true,
9 | "attr-no-duplication": true,
10 | "title-require": true,
11 | "tag-self-close": true,
12 | "head-script-disabled": true,
13 | "doctype-html5": true,
14 | "id-class-value": "dash",
15 | "style-disabled": true,
16 | "inline-style-disabled": true,
17 | "inline-script-disabled": true,
18 | "space-tab-mixed-disabled": "true",
19 | "id-class-ad-disabled": true,
20 | "attr-unsafe-chars": true
21 | }
22 |
--------------------------------------------------------------------------------
/.stylelintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": [
3 | "stylelint-config-standard",
4 | "stylelint-config-recommended-scss",
5 | "stylelint-config-prettier"
6 | ],
7 | "rules": {
8 | "font-family-name-quotes": "always-where-recommended",
9 | "function-url-quotes": [
10 | "always",
11 | {
12 | "except": ["empty"]
13 | }
14 | ],
15 | "selector-attribute-quotes": "always",
16 | "string-quotes": "double",
17 | "max-nesting-depth": 3,
18 | "selector-max-compound-selectors": 3,
19 | "selector-max-specificity": "0,3,2",
20 | "declaration-no-important": true,
21 | "at-rule-no-vendor-prefix": true,
22 | "media-feature-name-no-vendor-prefix": true,
23 | "property-no-vendor-prefix": true,
24 | "selector-no-vendor-prefix": true,
25 | "value-no-vendor-prefix": true,
26 | "no-empty-source": null,
27 | "selector-class-pattern": "[a-z-]+",
28 | "selector-id-pattern": "[a-z-]+",
29 | "selector-max-id": 0,
30 | "selector-no-qualifying-type": true,
31 | "selector-max-universal": 0,
32 | "selector-pseudo-element-no-unknown": [
33 | true,
34 | {
35 | "ignorePseudoElements": ["ng-deep"]
36 | }
37 | ],
38 | "unit-whitelist": ["px", "%", "em", "rem", "vw", "vh", "deg", "s"],
39 | "max-empty-lines": 2,
40 | "max-line-length": 120
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/.yo-rc.json:
--------------------------------------------------------------------------------
1 | {
2 | "generator-ngx-rocket": {
3 | "version": "5.2.0",
4 | "props": {
5 | "location": "path",
6 | "appName": "cynthesize",
7 | "target": [
8 | "web"
9 | ],
10 | "pwa": true,
11 | "ui": "material",
12 | "layout": "simple",
13 | "auth": true,
14 | "lazy": true,
15 | "angulartics": true,
16 | "analyticsProvider": "ga",
17 | "googleAnalyticsAccount": "UA-117554037-1",
18 | "prettier": true,
19 | "projectName": "cynthesize",
20 | "packageManager": "npm",
21 | "mobile": []
22 | }
23 | }
24 | }
--------------------------------------------------------------------------------
/app.yaml:
--------------------------------------------------------------------------------
1 | runtime: python27
2 | api_version: 1
3 | threadsafe: true
4 | handlers:
5 | - url: /
6 | static_files: dist/index.html
7 | upload: dist/index.html
8 | - url: /
9 | static_dir: dist
10 |
--------------------------------------------------------------------------------
/apple-touch-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Cynthesize/cynthesize-frontend/fc71507b4e7bd8a391956a73d4ced3aac87f0b37/apple-touch-icon.png
--------------------------------------------------------------------------------
/deploy.sh:
--------------------------------------------------------------------------------
1 | read -p "Enter the name of branch you want to deploy: " branch_name
2 | git reset --hard
3 | git checkout $branch_name
4 | git pull origin $branch_name
5 | npm install -g @angular/cli
6 |
7 | echo "Removing development index.html..."
8 | rm cynthesize-frontend/index.html
9 | echo "Copying production index.html..."
10 | cp ../index.html ./
11 |
12 | ng build --prod --aot
13 | cd ..
14 | rm -rf cynthesize/dist
15 | echo "Moving files to host distribution folder..."
16 | mv cynthesize-frontend/dist cynthesize
17 | cd cynthesize
18 | gcloud app deploy
19 |
--------------------------------------------------------------------------------
/docs/analytics.md:
--------------------------------------------------------------------------------
1 | # Analytics
2 |
3 | Analytics in this app are managed through the [Angulartics2](https://github.com/angulartics/angulartics2) library.
4 |
5 | It is already pre-configured to track page views, and provides examples to track events from both TypeScript code and HTML elements.
6 | Here is a quick usage documentation, you can read further information on the official website.
7 |
8 | ## Registering your provider
9 |
10 | Google Analytics is already registered as the project's analytics provider.
11 | Should you need to change the account identifier, you can do so in the call to `ga(...)` performed in the body of `index.html`.
12 |
13 | ## Tracking events
14 |
15 | ### Declarative event tracking
16 |
17 | The simplest way to do event tracking is by adding the attributes `angulartics2On`, `angularticsCategory` and `angularticsAction` to an HTML element.
18 | The homepage generated by the starter kit contains one such button.
19 | For reference, here is a UI-framework-agnostic example.
20 |
21 | ```html
22 |
28 | ```
29 |
30 | ### Using the API
31 |
32 | As an example, the application already comes configured to track its startup through an event.
33 | You may use the example as reference: it can be found in the first lines of `ngOnInit()` in `app.component.ts`.
34 |
35 | To access the API, inject your provider :
36 |
37 | ```typescript
38 | constructor(...
39 | private angulartics2GoogleAnalytics: Angulartics2GoogleAnalytics,
40 | ...)
41 | ```
42 |
43 | You may then use the `eventTrack` function:
44 |
45 | ```typescript
46 | this.angulartics2GoogleAnalytics.eventTrack('Something happened', {category: 'My category'});
47 | this.angulartics2GoogleAnalytics.eventTrack('Something else happened', {category: 'My other category', label: 'My custom label'});
48 | ```
49 |
50 |
--------------------------------------------------------------------------------
/docs/backend-proxy.md:
--------------------------------------------------------------------------------
1 | # Backend proxy
2 |
3 | Usually when working on a web application you consume data from custom-made APIs.
4 |
5 | To ease development with our development server integrating live reload while keeping your backend API calls working,
6 | we also have setup a backend proxy to redirect API calls to whatever URL and port you want. This allows you:
7 |
8 | - To develop frontend features without the need to run an API backend locally
9 | - To use a local development server without [CORS](https://en.wikipedia.org/wiki/Cross-origin_resource_sharing) issues
10 | - To debug frontend code with data from a remote testing platform directly
11 |
12 | ## How to configure
13 |
14 | In the root folder you will find a `proxy.conf.js`, containing the backend proxy configuration.
15 |
16 | The interesting part is there:
17 | ```js
18 | const proxyConfig = [
19 | {
20 | context: '/api',
21 | pathRewrite: {'^/api': ''},
22 | target: 'http://api.icndb.com',
23 | changeOrigin: true
24 | }
25 | ];
26 | ```
27 |
28 | This is where you can setup one or more proxy rules.
29 |
30 | For the complete set of options, see the `http-proxy-middleware`
31 | [documentation](https://github.com/chimurai/http-proxy-middleware#options).
32 |
33 | ### Corporate proxy support
34 |
35 | To allow external API calls redirection through a corporate proxy, you will also find a `setupForCorporateProxy()`
36 | function in the proxy configuration file. By default, this method configures a corporate proxy agent based on the
37 | `HTTP_PROXY` environment variable, see the [corporate proxy documentation](corporate-proxy.md) for more details.
38 |
39 | If you need to, you can further customize this function to fit the network of your working environment.
40 |
41 | If your corporate proxy use a custom SSL certificate, your may need to add the `secure: false` option to your
42 | backend proxy configuration.
43 |
--------------------------------------------------------------------------------
/docs/coding-guides/html.md:
--------------------------------------------------------------------------------
1 | # HTML coding guide
2 |
3 | ## Naming conventions
4 |
5 | - Everything should be named in `kebab-case` (lowercase words separated with a `-`): tags, attributes, IDs, etc,
6 | **except for everything bound to Angular** such variables, directives or events which should be in `camelCase`
7 | - File names should always be in `kebab-case`
8 |
9 | ## Coding rules
10 |
11 | - Use HTML5 doctype: ``
12 | - Use HTML [semantic elements](https://developer.mozilla.org/docs/Web/HTML/Sections_and_Outlines_of_an_HTML5_document)
13 | - Use double quotes `"` around attribute values in tags
14 | - Use a new line for every block, list, or table element, and indent every such child element
15 | - Clearly Separate structure (HTML) from presentation (CSS) from behavior (JavaScript):
16 | * Never use inline CSS or JavaScript
17 | * Keep any logic out of the HTML
18 | - `type` attribute for stylesheets and script tags should be omitted
19 |
20 | ## Common pitfalls
21 |
22 | - **Block**-type tags cannot be nested inside **inline**-type tags: a `
` tag cannot be nested in a ``.
23 | This rule also applies regarding the `display` value of an element.
24 | - HTML is **not** XML: empty tags cannot be self-closing and will result in improper results
25 | * `` will be interpreted as a simple `
` without closing tag!
26 | * The only tags that allows self-closing are the one that does not require a closing tag in first place:
27 | these are the void elements that do not not accept content ` `, ``, ``, ``, ``, ``
28 | (and others).
29 |
30 | ## Templates
31 |
32 | In accordance with the [Angular style guide](https://angular.io/guide/styleguide), HTML templates should be extracted in
33 | separate files, when more than 3 lines.
34 |
35 | Only use inline templates sparingly in very simple components with less than 3 lines of HTML.
36 |
37 | ## Enforcement
38 |
39 | Coding rules enforcement and basic sanity checks are done in this project by [HTMLHint](http://htmlhint.com).
40 |
41 |
--------------------------------------------------------------------------------
/docs/corporate-proxy.md:
--------------------------------------------------------------------------------
1 | # Working behind a corporate proxy
2 |
3 | ## Environment
4 |
5 | Most tools (including npm and git) use the `HTTP_PROXY` and `HTTPS_PROXY` environment variables to work with a
6 | corporate proxy.
7 |
8 | ### Windows
9 |
10 | In Windows environments, add the `HTTP_PROXY` and `HTTPS_PROXY` system environment variable, with these values:
11 |
12 | - HTTP_PROXY: `http://:@:`
13 | - HTTPS_PROXY: `%HTTP_PROXY%`
14 |
15 | ### Unix
16 |
17 | Add these lines to your `~/.bash_profile` or `~/.profile`:
18 | ```sh
19 | export HTTP_PROXY="http://:@:"
20 | export HTTPS_PROXY="$HTTP_PROXY"
21 | ```
22 |
23 | ## Proxy with SSL custom certificate
24 |
25 | Some proxy like **zscaler** use a custom SSL certificate to inspect request, which may cause npm commands to fail.
26 |
27 | To solve this problem, you can disable the `strict-ssl` option in npm.
28 |
29 | ## Proxy exceptions
30 |
31 | If you need to access repositories on your local network that should bypass proxy, set the `NO_PROXY` environment
32 | variable, in the same way as `HTTP_PROXY`:
33 |
34 | ### Windows
35 |
36 | - NO_PROXY: `127.0.0.1, localhost, `
37 |
38 | ### Unix
39 |
40 | ```sh
41 | export NO_PROXY="127.0.0.1, localhost, "
42 | ```
43 |
44 | ### Npm
45 |
46 | Run this command in your project directory:
47 | ```sh
48 | npm set strict-ssl false
49 | ```
50 |
--------------------------------------------------------------------------------
/docs/i18n.md:
--------------------------------------------------------------------------------
1 | # I18n
2 |
3 | The internationalization of the application is managed by [ngx-translate](https://github.com/ngx-translate/core).
4 |
5 | ## Adding translatable strings
6 |
7 | ### In HTML templates
8 |
9 | Use the `translate` directive on an HTML element to automatically translate its content:
10 | ```html
11 | This text will be translated.
12 | ```
13 |
14 | You can also use the `translate` pipe if needed:
15 | ```html
16 |
17 | ```
18 |
19 | ### In TypeScript code
20 |
21 | If you need to translate strings in JavaScript code, import the `TranslateService` dependency and use the asynchronous
22 | `get()` method:
23 |
24 | ```typescript
25 | let title;
26 | translateService.get('My page title').subscribe((res: string) => { title = res; });
27 | ```
28 |
29 | ## Extracting strings to translate
30 |
31 | Once you are ready to translate your app, just run `npm run translations:extract`.
32 | It will create a `template.json` file in the `src/translations` folder.
33 |
34 | You can then use any text or code editor to generate the `.json` files for each of your supported languages, and put
35 | them in the `src/translations` folder.
36 |
37 | Do no forget to edit the files in `src/environment` to add the supported languages of your application.
38 |
39 | ### Marking strings for extraction
40 |
41 | If strings are not directly passed to `translateService` or put in HTML templates, they may be missing from the
42 | extraction process.
43 |
44 | For these cases, you have to use the dummy `extract()` function:
45 | ```typescript
46 | import { extract } from './core/i18n.service';
47 |
48 | function toBeTranslatedLater() {
49 | return extract('A string to be translated');
50 | }
51 | ```
52 |
53 | Strings marked like this will then be properly extracted.
54 |
--------------------------------------------------------------------------------
/docs/readme.md:
--------------------------------------------------------------------------------
1 | # cynthesize
2 |
3 | Welcome to the project documentation!
4 |
5 | Use `npm run docs` for easier navigation.
6 |
7 | ## Available documentation
8 |
9 | [[index]]
10 |
--------------------------------------------------------------------------------
/docs/routing.md:
--------------------------------------------------------------------------------
1 | # Browser routing
2 |
3 | To allow navigation without triggering a server request, Angular now use by default the
4 | [HTML5 pushState](https://developer.mozilla.org/en-US/docs/Web/API/History_API#Adding_and_modifying_history_entries)
5 | API enabling natural URL style (like `localhost:4200/home/`), in opposition to Angular 1 which used the *hashbang* hack
6 | routing style (like `localhost:4200/#/home/`).
7 |
8 | This change has several consequences you should know of, be sure to read the
9 | [browser URL styles](https://angular.io/docs/ts/latest/guide/router.html#!#browser-url-styles) notice to fully
10 | understand the differences between the two approaches.
11 |
12 | In short:
13 |
14 | - It is only supported on modern browsers (IE10+), a [polyfill](https://github.com/Modernizr/Modernizr/wiki/HTML5-Cross-Browser-Polyfills#html5-history-api-pushstate-replacestate-popstate)
15 | is required for older browsers.
16 |
17 | - You have the option to perform *server-side rendering* later if you need to increase your app perceived performance.
18 |
19 | - You need to [configure URL rewriting](#server-configuration) on your server so that all routes serve your index file.
20 |
21 | It is still possible to revert to the hash strategy, but unless you have specific needs, you should stick with the
22 | default HTML5 routing mode.
23 |
24 | ## Server configuration
25 |
26 | To allow your angular application working properly as a *Single Page Application* (SPA) and allow bookmarking or
27 | refreshing any page, you need some configuration on your server, otherwise you will be running into troubles.
28 |
29 | > Note that during development, the live reload server already supports SPA mode.
30 |
31 | The basic idea is simply to serve the `index.html` file for every request aimed at your application.
32 |
33 | Here is an example on how to perform this on an [Express](http://expressjs.com) NodeJS server:
34 |
35 | ```js
36 | // Put this in your `server.js` file, after your other rules (APIs, static files...)
37 | app.get('/*', function(req, res) {
38 | res.sendFile(__dirname + '/index.html')
39 | });
40 | ```
41 |
42 | For other servers like [Nginx](https://www.nginx.com/blog/creating-nginx-rewrite-rules/) or
43 | [Apache](http://httpd.apache.org/docs/2.0/misc/rewriteguide.html), you may look for how to perform *URL rewriting*.
44 |
--------------------------------------------------------------------------------
/docs/updating.md:
--------------------------------------------------------------------------------
1 | # Updating npm dependencies
2 |
3 | - Check outdated packages
4 | ```sh
5 | npm outdated
6 | ```
7 |
8 | - Update local packages according to `package.json`
9 | ```sh
10 | npm update
11 | ```
12 |
13 | - Upgrade packages manually
14 | ```sh
15 | npm install --save[-dev] @latest
16 | ```
17 |
18 | Alternatively, you can use [npm-check](https://github.com/dylang/npm-check) to perform an interactive upgrade:
19 | ```sh
20 | npm-check -u --skip-unused
21 | ```
22 |
23 | ## Locking package versions
24 |
25 | Starting from `npm@5` a new `package-lock.json` file is
26 | [automatically generated](https://docs.npmjs.com/files/package-locks) when using `npm install` commands, to ensure a
27 | reproducible dependency tree and avoid unwanted package updates.
28 |
29 | If you use a previous npm version, it is recommended to use [npm shrinkwrap](https://docs.npmjs.com/cli/shrinkwrap) to
30 | lock down all your dependencies version:
31 | ```sh
32 | npm shrinkwrap --dev
33 | ```
34 |
35 | This will create a file `npm-shrinkwrap.json` alongside your `package.json` files.
36 |
37 | > Do not forget to run shrinkwrap each time you manually update your dependencies!
38 |
39 | # Updating angular-related dependencies
40 |
41 | See the [Angular update website](https://update.angular.io) to guide you through the updating/upgrading steps.
42 |
--------------------------------------------------------------------------------
/e2e/protractor.conf.js:
--------------------------------------------------------------------------------
1 | // Protractor configuration file, see link for more information
2 | // https://github.com/angular/protractor/blob/master/lib/config.ts
3 |
4 | const { SpecReporter } = require('jasmine-spec-reporter');
5 |
6 | exports.config = {
7 | allScriptsTimeout: 11000,
8 | specs: ['./src/**/*.e2e-spec.ts'],
9 | capabilities: {
10 | browserName: process.env.PROTRACTOR_BROWSER || 'chrome'
11 | },
12 | // Only works with Chrome and Firefox
13 | directConnect: true,
14 | baseUrl: 'http://localhost:4200/',
15 | framework: 'jasmine2',
16 | jasmineNodeOpts: {
17 | showColors: true,
18 | defaultTimeoutInterval: 30000,
19 | print: function() {}
20 | },
21 | onPrepare() {
22 | require('ts-node').register({
23 | project: require('path').join(__dirname, './tsconfig.e2e.json')
24 | });
25 | // Better console spec reporter
26 | jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));
27 | }
28 | };
29 |
--------------------------------------------------------------------------------
/e2e/src/app.e2e-spec.ts:
--------------------------------------------------------------------------------
1 | import { browser } from 'protractor';
2 | import { AppPage } from './app.po';
3 |
4 | describe('app', () => {
5 | let page: AppPage;
6 |
7 | beforeEach(() => {
8 | page = new AppPage();
9 | });
10 |
11 | it('should display login page and login into app', () => {
12 | page.navigateTo();
13 | expect(browser.getCurrentUrl()).toContain('/login');
14 | page.login();
15 | });
16 |
17 | it('should display hello message', () => {
18 | page.navigateTo();
19 | expect(page.getParagraphText()).toEqual('Hello world !');
20 | });
21 | });
22 |
--------------------------------------------------------------------------------
/e2e/src/app.po.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Use the Page Object pattern to define the page under test.
3 | * See docs/coding-guide/e2e-tests.md for more info.
4 | */
5 |
6 | import { browser, element, by } from 'protractor';
7 |
8 | export class AppPage {
9 | usernameField = element(by.css('input[formControlName="username"]'));
10 | passwordField = element(by.css('input[formControlName="password"]'));
11 | loginButton = element(by.css('button[type="submit"]'));
12 |
13 | constructor() {
14 | // Forces default language
15 | this.navigateTo();
16 | browser.executeScript(() => localStorage.setItem('language', 'en-US'));
17 | }
18 |
19 | navigateTo() {
20 | return browser.get('/');
21 | }
22 |
23 | login() {
24 | this.usernameField.sendKeys('test');
25 | this.passwordField.sendKeys('123');
26 | this.loginButton.click();
27 | }
28 |
29 | getParagraphText() {
30 | return element(by.css('app-root mat-card-title')).getText();
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/e2e/tsconfig.e2e.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../out-tsc/e2e",
5 | "baseUrl": "./",
6 | "module": "commonjs",
7 | "target": "es5",
8 | "types": [
9 | "jasmine",
10 | "jasminewd2",
11 | "node"
12 | ]
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/ngsw-config.json:
--------------------------------------------------------------------------------
1 | {
2 | "index": "/index.html",
3 | "assetGroups": [
4 | {
5 | "name": "app",
6 | "installMode": "prefetch",
7 | "resources": {
8 | "files": [
9 | "/favicon.ico",
10 | "/index.html",
11 | "/*.css",
12 | "/*.js"
13 | ]
14 | }
15 | },
16 | {
17 | "name": "assets",
18 | "installMode": "lazy",
19 | "updateMode": "prefetch",
20 | "resources": {
21 | "files": [
22 | "/assets/**"
23 | ]
24 | }
25 | }
26 | ]
27 | }
28 |
--------------------------------------------------------------------------------
/proxy.conf.json:
--------------------------------------------------------------------------------
1 | {
2 | "/api": {
3 | "target": "http://127.0.0.1:8000/",
4 | "secure": false,
5 | "changeOrigin": true
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/public-schema1.sql:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Cynthesize/cynthesize-frontend/fc71507b4e7bd8a391956a73d4ced3aac87f0b37/public-schema1.sql
--------------------------------------------------------------------------------
/src/_redirects:
--------------------------------------------------------------------------------
1 | # src/_redirects
2 |
3 | /* /index.html 200
4 |
--------------------------------------------------------------------------------
/src/app/add/add-project/add-project.component.scss:
--------------------------------------------------------------------------------
1 | .add-idea-card {
2 | padding: 2rem;
3 | }
4 |
5 | .field {
6 | width: 100%;
7 | min-width: -webkit-fill-available;
8 | }
9 |
10 | h3 {
11 | display: flex;
12 | align-items: center;
13 | }
14 |
15 | h3 > mat-icon {
16 | padding: 3px 0 0 8px;
17 | font-size: 1.2rem;
18 | align-self: center;
19 | }
20 |
21 | .add-project {
22 | padding: 30px 50px 0 50px;
23 | background: white;
24 | margin-top: 25px;
25 | width: 100%;
26 | border-radius: 5px;
27 | }
28 |
29 | .add-project-image {
30 | flex: 1;
31 | }
32 |
33 | .add-project-image img {
34 | width: 100%;
35 | }
36 |
37 | .add-project-card-form {
38 | flex: 1;
39 | }
40 | @media (max-width: 750px) {
41 | .add-project-image {
42 | display: none;
43 | }
44 | .add-project-card-form {
45 | width: 100%;
46 | }
47 | }
48 |
49 | .img-field {
50 | display: flex;
51 | }
52 |
53 | .img-field h3 {
54 | margin-right: 25px;
55 | }
56 |
57 | .avatar-upload {
58 | position: relative;
59 | max-width: 205px;
60 | .avatar-edit {
61 | position: absolute;
62 | left: 85px;
63 | z-index: 1;
64 | input {
65 | display: none;
66 | + label {
67 | display: inline-block;
68 | width: 34px;
69 | height: 34px;
70 | margin-bottom: 0;
71 | border-radius: 100%;
72 | background: #f7f7f7;
73 | border: 1px solid transparent;
74 | box-shadow: 0px 2px 4px 0px rgba(0, 0, 0, 0.12);
75 | cursor: pointer;
76 | font-weight: normal;
77 | transition: all 0.2s ease-in-out;
78 | &:hover {
79 | background: #f1f1f1;
80 | border-color: #d6d6d6;
81 | }
82 | &:after {
83 | content: "\f040";
84 | font-family: "FontAwesome";
85 | color: #757575;
86 | position: absolute;
87 | top: 10px;
88 | left: 0;
89 | right: 0;
90 | text-align: center;
91 | margin: auto;
92 | }
93 | }
94 | }
95 | }
96 | .avatar-preview {
97 | width: 125px;
98 | height: 125px;
99 | position: relative;
100 | border-radius: 100%;
101 | border: 6px solid #f8f8f8;
102 | box-shadow: 0px 2px 4px 0px rgba(0, 0, 0, 0.1);
103 | > div {
104 | width: 100%;
105 | height: 100%;
106 | border-radius: 100%;
107 | background-size: cover;
108 | background-repeat: no-repeat;
109 | background-position: center;
110 | }
111 | }
112 | }
113 |
--------------------------------------------------------------------------------
/src/app/add/add-project/add-project.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { AddProjectComponent } from './add-project.component';
4 |
5 | describe('AddProjectComponent', () => {
6 | let component: AddProjectComponent;
7 | let fixture: ComponentFixture;
8 |
9 | beforeEach(async(() => {
10 | TestBed.configureTestingModule({
11 | declarations: [AddProjectComponent]
12 | }).compileComponents();
13 | }));
14 |
15 | beforeEach(() => {
16 | fixture = TestBed.createComponent(AddProjectComponent);
17 | component = fixture.componentInstance;
18 | fixture.detectChanges();
19 | });
20 |
21 | it('should create', () => {
22 | expect(component).toBeTruthy();
23 | });
24 | });
25 |
--------------------------------------------------------------------------------
/src/app/add/add-routing.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { Routes, RouterModule } from '@angular/router';
3 | import { AddProjectComponent } from './add-project/add-project.component';
4 | import { MentorComponent } from './mentor/mentor.component';
5 |
6 | const routes: Routes = [
7 | {
8 | path: '',
9 | children: [{ path: 'project', component: AddProjectComponent }, { path: 'mentor', component: MentorComponent }]
10 | }
11 | ];
12 |
13 | @NgModule({
14 | imports: [RouterModule.forChild(routes)],
15 | exports: [RouterModule]
16 | })
17 | export class AddRoutingModule {}
18 |
--------------------------------------------------------------------------------
/src/app/add/add.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { CommonModule } from '@angular/common';
3 |
4 | import { AddRoutingModule } from './add-routing.module';
5 | import { AddProjectComponent } from './add-project/add-project.component';
6 | import { SharedModule } from '@app/shared';
7 | import { MaterialModule } from '@app/material.module';
8 | import { FormsModule, ReactiveFormsModule } from '@angular/forms';
9 | import { CovalentTextEditorModule } from '@covalent/text-editor';
10 | import { CovalentMarkdownModule } from '@covalent/markdown';
11 | import { MentorComponent } from './mentor/mentor.component';
12 |
13 | @NgModule({
14 | declarations: [AddProjectComponent, MentorComponent],
15 | imports: [
16 | CommonModule,
17 | AddRoutingModule,
18 | SharedModule,
19 | MaterialModule,
20 | FormsModule,
21 | ReactiveFormsModule,
22 | CovalentTextEditorModule,
23 | CovalentMarkdownModule
24 | ]
25 | })
26 | export class AddModule {}
27 |
--------------------------------------------------------------------------------
/src/app/add/mentor/mentor.component.scss:
--------------------------------------------------------------------------------
1 | .add-mentor {
2 | display: flex;
3 | justify-content: center;
4 | align-items: center;
5 | height: 100%;
6 | background: white;
7 | }
8 |
9 | .add-mentor-right {
10 | height: 93.5vh;
11 | flex: 1.55;
12 | display: flex;
13 | background: #737373;
14 | justify-content: center;
15 | }
16 |
17 | .add-mentor-right > img {
18 | width: 80%;
19 | }
20 |
21 | .add-mentor-left {
22 | display: flex;
23 | flex: 1;
24 | justify-content: center;
25 | }
26 |
27 | .add-mentor-heading {
28 | margin-bottom: 1rem;
29 | }
30 |
31 | .add-mentor-stepper {
32 | display: flex;
33 | justify-content: flex-end;
34 | }
35 |
36 | .already-applied {
37 | display: flex;
38 | flex-direction: column;
39 | align-items: center;
40 | justify-content: center;
41 | }
42 |
43 | .already-applied > img {
44 | width: 30%;
45 | margin-bottom: 30px;
46 | }
47 |
--------------------------------------------------------------------------------
/src/app/add/mentor/mentor.component.spec.ts:
--------------------------------------------------------------------------------
1 | /* tslint:disable:no-unused-variable */
2 | import { async, ComponentFixture, TestBed } from '@angular/core/testing';
3 |
4 | import { MentorComponent } from './mentor.component';
5 |
6 | describe('MentorComponent', () => {
7 | let component: MentorComponent;
8 | let fixture: ComponentFixture;
9 |
10 | beforeEach(async(() => {
11 | TestBed.configureTestingModule({
12 | declarations: [MentorComponent]
13 | }).compileComponents();
14 | }));
15 |
16 | beforeEach(() => {
17 | fixture = TestBed.createComponent(MentorComponent);
18 | component = fixture.componentInstance;
19 | fixture.detectChanges();
20 | });
21 |
22 | it('should create', () => {
23 | expect(component).toBeTruthy();
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/src/app/animations/router.animations.ts:
--------------------------------------------------------------------------------
1 | import { trigger, animate, style, group, animateChild, query, stagger, transition } from '@angular/animations';
2 |
3 | export const routerTransition = trigger('routerTransition', [
4 | transition('* <=> *', [
5 | /* order */
6 | /* 1 */ query(':enter, :leave', style({ position: 'fixed', width: '100%' }), { optional: true }),
7 | /* 2 */ group([
8 | // block executes in parallel
9 | query(
10 | ':enter',
11 | [style({ transform: 'translateX(100%)' }), animate('0.5s ease-in-out', style({ transform: 'translateX(0%)' }))],
12 | { optional: true }
13 | ),
14 | query(
15 | ':leave',
16 | [
17 | style({ transform: 'translateX(0%)' }),
18 | animate('0.5s ease-in-out', style({ transform: 'translateX(-100%)' }))
19 | ],
20 | { optional: true }
21 | )
22 | ])
23 | ])
24 | ]);
25 |
--------------------------------------------------------------------------------
/src/app/app-routing.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { Routes, RouterModule, PreloadAllModules } from '@angular/router';
3 | import { Shell } from '@app/shell/shell.service';
4 | import { NotFoundComponent } from './shared/not-found/not-found.component';
5 | import { UnauthorisedComponent } from './shared/unauthorized/unauthorized.component';
6 |
7 | const routes: Routes = [
8 | Shell.childRoutes([
9 | { path: 'add', loadChildren: 'app/add/add.module#AddModule', data: { state: 'add' } },
10 | { path: 'view', loadChildren: 'app/view/view.module#ViewModule', data: { state: 'view' } },
11 | { path: 'user', loadChildren: 'app/profile/profile.module#ProfileModule', data: { state: 'user' } },
12 | { path: 'not-found', component: NotFoundComponent, data: { title: 'Page not found! :(', state: 'not-found' } },
13 | {
14 | path: 'unauthorized',
15 | component: UnauthorisedComponent,
16 | data: { title: 'Unauthorised Access', state: 'unauthorized' }
17 | }
18 | ]),
19 | // Fallback when no prior route is matched
20 | { path: '**', redirectTo: '', pathMatch: 'full' }
21 | ];
22 |
23 | @NgModule({
24 | imports: [RouterModule.forRoot(routes, { preloadingStrategy: PreloadAllModules })],
25 | exports: [RouterModule],
26 | providers: []
27 | })
28 | export class AppRoutingModule {}
29 |
--------------------------------------------------------------------------------
/src/app/app.component.html:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/src/app/app.component.scss:
--------------------------------------------------------------------------------
1 | :host {
2 | display: flex;
3 | flex: 1;
4 | }
5 |
--------------------------------------------------------------------------------
/src/app/app.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { TestBed, async } from '@angular/core/testing';
2 | import { RouterTestingModule } from '@angular/router/testing';
3 | import { TranslateModule } from '@ngx-translate/core';
4 | import { Angulartics2Module } from 'angulartics2';
5 | import { Angulartics2GoogleAnalytics } from 'angulartics2/ga';
6 |
7 | import { CoreModule } from '@app/core';
8 | import { AppComponent } from './app.component';
9 |
10 | describe('AppComponent', () => {
11 | beforeEach(async(() => {
12 | TestBed.configureTestingModule({
13 | imports: [
14 | Angulartics2Module.forRoot([Angulartics2GoogleAnalytics]),
15 | RouterTestingModule,
16 | TranslateModule.forRoot(),
17 | CoreModule
18 | ],
19 | declarations: [AppComponent],
20 | providers: []
21 | });
22 | TestBed.compileComponents();
23 | }));
24 |
25 | it('should create the app', async(() => {
26 | const fixture = TestBed.createComponent(AppComponent);
27 | const app = fixture.debugElement.componentInstance;
28 | expect(app).toBeTruthy();
29 | }));
30 | });
31 |
--------------------------------------------------------------------------------
/src/app/app.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from '@angular/core';
2 | import { Angulartics2GoogleAnalytics } from 'angulartics2/ga';
3 |
4 | import { environment } from '@env/environment';
5 | import { Logger, AuthenticationService } from '@app/core';
6 | import { ErrorHandlerService } from './core/error-handler.service';
7 | import { MatSnackBar } from '@angular/material';
8 |
9 | const log = new Logger('App');
10 |
11 | @Component({
12 | selector: 'app-root',
13 | templateUrl: './app.component.html',
14 | styleUrls: ['./app.component.scss']
15 | })
16 | export class AppComponent implements OnInit {
17 | constructor(
18 | private angulartics2GoogleAnalytics: Angulartics2GoogleAnalytics,
19 | private errorHandler: ErrorHandlerService,
20 | private snackBar: MatSnackBar,
21 | private authService: AuthenticationService
22 | ) {
23 | this.authService.handleAuthentication();
24 | angulartics2GoogleAnalytics.startTracking();
25 | this.errorHandler.subj_notification.subscribe(message => {
26 | let messageString = '';
27 | if (typeof message === 'string') {
28 | messageString = message;
29 | } else if (!message.message.indexOf('Network')) {
30 | messageString =
31 | 'There appears to be something wrong with your internet connection. Please check and try again.';
32 | } else {
33 | messageString = message.message;
34 | }
35 | this.snackBar.open(messageString, 'Okay', {
36 | duration: 15000
37 | });
38 | });
39 | }
40 |
41 | ngOnInit() {
42 | // Setup logger
43 | if (environment.production) {
44 | Logger.enableProductionMode();
45 | }
46 |
47 | this.angulartics2GoogleAnalytics.eventTrack(environment.version, { category: 'App initialized' });
48 | if (this.authService.isAuthenticated()) {
49 | this.authService.renewTokens();
50 | } else {
51 | this.authService.softLogout();
52 | }
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/src/app/app.module.ts:
--------------------------------------------------------------------------------
1 | import { BrowserModule } from '@angular/platform-browser';
2 | import { NgModule } from '@angular/core';
3 | import { FormsModule } from '@angular/forms';
4 | import { HttpClientModule } from '@angular/common/http';
5 | import { ServiceWorkerModule } from '@angular/service-worker';
6 | import { TranslateModule } from '@ngx-translate/core';
7 | import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
8 | import { MaterialModule } from './material.module';
9 | import { Angulartics2Module } from 'angulartics2';
10 |
11 | import { environment } from '@env/environment';
12 | import { CoreModule } from '@app/core';
13 | import { SharedModule } from '@app/shared';
14 | import { HomeModule } from './home/home.module';
15 | import { ShellModule } from './shell/shell.module';
16 | import { AuthModule } from './auth/auth.module';
17 | import { AppComponent } from './app.component';
18 | import { AppRoutingModule } from './app-routing.module';
19 |
20 | @NgModule({
21 | imports: [
22 | BrowserModule,
23 | ServiceWorkerModule.register('./ngsw-worker.js', { enabled: environment.production }),
24 | FormsModule,
25 | HttpClientModule,
26 | TranslateModule.forRoot(),
27 | BrowserAnimationsModule,
28 | MaterialModule,
29 | CoreModule,
30 | SharedModule,
31 | ShellModule,
32 | HomeModule,
33 | AuthModule,
34 | Angulartics2Module.forRoot(),
35 | AppRoutingModule // must be imported as the last module as it contains the fallback route
36 | ],
37 | declarations: [AppComponent],
38 | providers: [],
39 | bootstrap: [AppComponent]
40 | })
41 | export class AppModule {}
42 |
--------------------------------------------------------------------------------
/src/app/auth/auth-routing.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { Routes, RouterModule } from '@angular/router';
3 |
4 | import { LoginComponent } from './login/login.component';
5 |
6 | const routes: Routes = [
7 | {
8 | path: 'login',
9 | component: LoginComponent,
10 | data: { title: 'Login' }
11 | }
12 | ];
13 |
14 | @NgModule({
15 | imports: [RouterModule.forChild(routes)],
16 | exports: [RouterModule],
17 | providers: []
18 | })
19 | export class LoginRoutingModule {}
20 |
--------------------------------------------------------------------------------
/src/app/auth/auth.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { CommonModule } from '@angular/common';
3 | import { ReactiveFormsModule } from '@angular/forms';
4 | import { TranslateModule } from '@ngx-translate/core';
5 | import { FlexLayoutModule } from '@angular/flex-layout';
6 |
7 | import { SharedModule } from '@app/shared';
8 | import { MaterialModule } from '@app/material.module';
9 | import { LoginRoutingModule } from './auth-routing.module';
10 | import { LoginComponent } from './login/login.component';
11 |
12 | @NgModule({
13 | imports: [
14 | CommonModule,
15 | ReactiveFormsModule,
16 | TranslateModule,
17 | SharedModule,
18 | FlexLayoutModule,
19 | MaterialModule,
20 | LoginRoutingModule
21 | ],
22 | declarations: [LoginComponent]
23 | })
24 | export class AuthModule {}
25 |
--------------------------------------------------------------------------------
/src/app/auth/login/login.component.html:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/src/app/auth/login/login.component.scss:
--------------------------------------------------------------------------------
1 | @import "../../../sass/declarations";
2 | @import "../../../sass/components/login-register";
3 |
--------------------------------------------------------------------------------
/src/app/auth/login/login.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing';
2 | import { TranslateModule } from '@ngx-translate/core';
3 | import { RouterTestingModule } from '@angular/router/testing';
4 | import { ReactiveFormsModule } from '@angular/forms';
5 | import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
6 | import { FlexLayoutModule } from '@angular/flex-layout';
7 |
8 | import { CoreModule } from '@app/core';
9 | import { SharedModule } from '@app/shared';
10 | import { MaterialModule } from '@app/material.module';
11 | import { LoginComponent } from './login.component';
12 |
13 | describe('LoginComponent', () => {
14 | let component: LoginComponent;
15 | let fixture: ComponentFixture;
16 |
17 | beforeEach(async(() => {
18 | TestBed.configureTestingModule({
19 | imports: [
20 | BrowserAnimationsModule,
21 | FlexLayoutModule,
22 | MaterialModule,
23 | SharedModule,
24 | RouterTestingModule,
25 | TranslateModule.forRoot(),
26 | ReactiveFormsModule,
27 | CoreModule
28 | ],
29 | declarations: [LoginComponent]
30 | }).compileComponents();
31 | }));
32 |
33 | beforeEach(() => {
34 | fixture = TestBed.createComponent(LoginComponent);
35 | component = fixture.componentInstance;
36 | fixture.detectChanges();
37 | });
38 |
39 | it('should create', () => {
40 | expect(component).toBeTruthy();
41 | });
42 | });
43 |
--------------------------------------------------------------------------------
/src/app/auth/login/login.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from '@angular/core';
2 | import { FormGroup } from '@angular/forms';
3 |
4 | import { environment } from '@env/environment';
5 | import { AuthenticationService } from '@app/core';
6 |
7 | @Component({
8 | selector: 'app-login',
9 | templateUrl: './login.component.html',
10 | styleUrls: ['./login.component.scss']
11 | })
12 | export class LoginComponent implements OnInit {
13 | version: string = environment.version;
14 | errorString: string;
15 | loginForm: FormGroup;
16 | isLoading = false;
17 |
18 | constructor(public authenticationService: AuthenticationService) {}
19 |
20 | ngOnInit() {}
21 | }
22 |
--------------------------------------------------------------------------------
/src/app/core/authentication/authentication.guard.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@angular/core';
2 | import { Router, CanActivate } from '@angular/router';
3 |
4 | import { Logger } from '../logger.service';
5 |
6 | const log = new Logger('AuthenticationGuard');
7 |
8 | @Injectable()
9 | export class AuthenticationGuard implements CanActivate {
10 | constructor(private router: Router) {}
11 |
12 | canActivate(): boolean {
13 | log.debug('Not authenticated, redirecting...');
14 | this.router.navigate(['/login'], { replaceUrl: true });
15 | return false;
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/app/core/comments/comments.service.spec.ts:
--------------------------------------------------------------------------------
1 | import { TestBed } from '@angular/core/testing';
2 |
3 | import { CommentsService } from './comments.service';
4 |
5 | describe('CommentsService', () => {
6 | beforeEach(() => TestBed.configureTestingModule({}));
7 |
8 | it('should be created', () => {
9 | const service: CommentsService = TestBed.get(CommentsService);
10 | expect(service).toBeTruthy();
11 | });
12 | });
13 |
--------------------------------------------------------------------------------
/src/app/core/core.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule, Optional, SkipSelf } from '@angular/core';
2 | import { CommonModule } from '@angular/common';
3 | import { HttpClient, HttpClientModule } from '@angular/common/http';
4 | import { RouteReuseStrategy, RouterModule } from '@angular/router';
5 | import { TranslateModule } from '@ngx-translate/core';
6 | import { RouteReusableStrategy } from './route-reusable-strategy';
7 | import { AuthenticationService } from './authentication/authentication.service';
8 | import { AuthenticationGuard } from './authentication/authentication.guard';
9 | import { HttpService } from './http/http.service';
10 | import { HttpCacheService } from './http/http-cache.service';
11 | import { ApiPrefixInterceptor } from './http/api-prefix.interceptor';
12 | import { ErrorHandlerInterceptor } from './http/error-handler.interceptor';
13 | import { CacheInterceptor } from './http/cache.interceptor';
14 |
15 | @NgModule({
16 | imports: [CommonModule, HttpClientModule, TranslateModule, RouterModule],
17 | providers: [
18 | AuthenticationService,
19 | AuthenticationGuard,
20 | HttpCacheService,
21 | ApiPrefixInterceptor,
22 | ErrorHandlerInterceptor,
23 | CacheInterceptor,
24 | {
25 | provide: HttpClient,
26 | useClass: HttpService
27 | },
28 | {
29 | provide: RouteReuseStrategy,
30 | useClass: RouteReusableStrategy
31 | }
32 | ]
33 | })
34 | export class CoreModule {
35 | constructor(
36 | @Optional()
37 | @SkipSelf()
38 | parentModule: CoreModule
39 | ) {
40 | // Import guard
41 | if (parentModule) {
42 | throw new Error(`${parentModule} has already been loaded. Import Core module in the AppModule only.`);
43 | }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/app/core/error-handler.service.spec.ts:
--------------------------------------------------------------------------------
1 | import { TestBed } from '@angular/core/testing';
2 |
3 | import { ErrorHandlerService } from './error-handler.service';
4 |
5 | describe('ErrorHandlerService', () => {
6 | beforeEach(() => TestBed.configureTestingModule({}));
7 |
8 | it('should be created', () => {
9 | const service: ErrorHandlerService = TestBed.get(ErrorHandlerService);
10 | expect(service).toBeTruthy();
11 | });
12 | });
13 |
--------------------------------------------------------------------------------
/src/app/core/error-handler.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@angular/core';
2 | import { Subject } from 'rxjs';
3 |
4 | @Injectable({
5 | providedIn: 'root'
6 | })
7 | export class ErrorHandlerService {
8 | public subj_notification: Subject = new Subject();
9 | public ideaWindowScrolled: Subject = new Subject();
10 |
11 | constructor() {}
12 | }
13 |
--------------------------------------------------------------------------------
/src/app/core/http/api-prefix.interceptor.spec.ts:
--------------------------------------------------------------------------------
1 | import { TestBed, inject } from '@angular/core/testing';
2 | import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
3 | import { HTTP_INTERCEPTORS, HttpClient } from '@angular/common/http';
4 |
5 | import { environment } from '@env/environment';
6 | import { ApiPrefixInterceptor } from './api-prefix.interceptor';
7 |
8 | describe('ApiPrefixInterceptor', () => {
9 | let http: HttpClient;
10 | let httpMock: HttpTestingController;
11 |
12 | beforeEach(() => {
13 | TestBed.configureTestingModule({
14 | imports: [HttpClientTestingModule],
15 | providers: [
16 | {
17 | provide: HTTP_INTERCEPTORS,
18 | useClass: ApiPrefixInterceptor,
19 | multi: true
20 | }
21 | ]
22 | });
23 | });
24 |
25 | beforeEach(inject([HttpClient, HttpTestingController], (_http: HttpClient, _httpMock: HttpTestingController) => {
26 | http = _http;
27 | httpMock = _httpMock;
28 | }));
29 |
30 | afterEach(() => {
31 | httpMock.verify();
32 | });
33 |
34 | it('should prepend environment.serverUrl to the request url', () => {
35 | // Act
36 | http.get('/toto').subscribe();
37 |
38 | // Assert
39 | httpMock.expectOne({ url: environment.serverUrl + '/toto' });
40 | });
41 |
42 | it('should not prepend environment.serverUrl to request url', () => {
43 | // Act
44 | http.get('hTtPs://domain.com/toto').subscribe();
45 |
46 | // Assert
47 | httpMock.expectOne({ url: 'hTtPs://domain.com/toto' });
48 | });
49 | });
50 |
--------------------------------------------------------------------------------
/src/app/core/http/api-prefix.interceptor.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@angular/core';
2 | import { HttpEvent, HttpInterceptor, HttpHandler, HttpRequest } from '@angular/common/http';
3 | import { Observable } from 'rxjs';
4 |
5 | import { environment } from '@env/environment';
6 |
7 | /**
8 | * Prefixes all requests with `environment.serverUrl`.
9 | */
10 | @Injectable()
11 | export class ApiPrefixInterceptor implements HttpInterceptor {
12 | intercept(request: HttpRequest, next: HttpHandler): Observable> {
13 | if (!/^(http|https):/i.test(request.url)) {
14 | request = request.clone({ url: environment.serverUrl + request.url });
15 | }
16 | return next.handle(request);
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/app/core/http/cache.interceptor.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@angular/core';
2 | import { HttpEvent, HttpInterceptor, HttpHandler, HttpRequest, HttpResponse } from '@angular/common/http';
3 | import { Observable, Subscriber } from 'rxjs';
4 |
5 | import { HttpCacheService } from './http-cache.service';
6 |
7 | /**
8 | * Caches HTTP requests.
9 | * Use ExtendedHttpClient fluent API to configure caching for each request.
10 | */
11 | @Injectable()
12 | export class CacheInterceptor implements HttpInterceptor {
13 | private forceUpdate = false;
14 |
15 | constructor(private httpCacheService: HttpCacheService) {}
16 |
17 | /**
18 | * Configures interceptor options
19 | * @param {{update: boolean}} options If update option is enabled, forces request to be made and updates cache entry.
20 | * @return {CacheInterceptor} The configured instance.
21 | */
22 | configure(options?: { update?: boolean } | null): CacheInterceptor {
23 | const instance = new CacheInterceptor(this.httpCacheService);
24 | if (options && options.update) {
25 | instance.forceUpdate = true;
26 | }
27 | return instance;
28 | }
29 |
30 | intercept(request: HttpRequest, next: HttpHandler): Observable> {
31 | if (request.method !== 'GET') {
32 | return next.handle(request);
33 | }
34 |
35 | return new Observable((subscriber: Subscriber>) => {
36 | const cachedData = this.forceUpdate ? null : this.httpCacheService.getCacheData(request.urlWithParams);
37 | if (cachedData !== null) {
38 | // Create new response to avoid side-effects
39 | subscriber.next(new HttpResponse(cachedData as Object));
40 | subscriber.complete();
41 | } else {
42 | next.handle(request).subscribe(
43 | event => {
44 | if (event instanceof HttpResponse) {
45 | this.httpCacheService.setCacheData(request.urlWithParams, event);
46 | }
47 | subscriber.next(event);
48 | },
49 | error => subscriber.error(error),
50 | () => subscriber.complete()
51 | );
52 | }
53 | });
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/src/app/core/http/error-handler.interceptor.spec.ts:
--------------------------------------------------------------------------------
1 | import { TestBed, inject } from '@angular/core/testing';
2 | import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
3 | import { HTTP_INTERCEPTORS, HttpClient } from '@angular/common/http';
4 |
5 | import { ErrorHandlerInterceptor } from './error-handler.interceptor';
6 |
7 | describe('ErrorHandlerInterceptor', () => {
8 | let errorHandlerInterceptor: ErrorHandlerInterceptor;
9 | let http: HttpClient;
10 | let httpMock: HttpTestingController;
11 |
12 | function createInterceptor() {
13 | errorHandlerInterceptor = new ErrorHandlerInterceptor();
14 | return errorHandlerInterceptor;
15 | }
16 |
17 | beforeEach(() => {
18 | TestBed.configureTestingModule({
19 | imports: [HttpClientTestingModule],
20 | providers: [
21 | {
22 | provide: HTTP_INTERCEPTORS,
23 | useFactory: createInterceptor,
24 | multi: true
25 | }
26 | ]
27 | });
28 | });
29 |
30 | beforeEach(inject([HttpClient, HttpTestingController], (_http: HttpClient, _httpMock: HttpTestingController) => {
31 | http = _http;
32 | httpMock = _httpMock;
33 | }));
34 |
35 | afterEach(() => {
36 | httpMock.verify();
37 | });
38 |
39 | it('should catch error and call error handler', () => {
40 | // Arrange
41 | // Note: here we spy on private method since target is customization here,
42 | // but you should replace it by actual behavior in your app
43 | spyOn(ErrorHandlerInterceptor.prototype as any, 'errorHandler').and.callThrough();
44 |
45 | // Act
46 | http.get('/toto').subscribe(
47 | () => fail('should error'),
48 | () => {
49 | // Assert
50 | expect(ErrorHandlerInterceptor.prototype['errorHandler']).toHaveBeenCalled();
51 | }
52 | );
53 |
54 | httpMock.expectOne({}).flush(null, {
55 | status: 404,
56 | statusText: 'error'
57 | });
58 | });
59 | });
60 |
--------------------------------------------------------------------------------
/src/app/core/http/error-handler.interceptor.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@angular/core';
2 | import { HttpEvent, HttpInterceptor, HttpHandler, HttpRequest } from '@angular/common/http';
3 | import { Observable } from 'rxjs';
4 | import { catchError } from 'rxjs/operators';
5 |
6 | import { environment } from '@env/environment';
7 | import { Logger } from '../logger.service';
8 |
9 | const log = new Logger('ErrorHandlerInterceptor');
10 |
11 | /**
12 | * Adds a default error handler to all requests.
13 | */
14 | @Injectable()
15 | export class ErrorHandlerInterceptor implements HttpInterceptor {
16 | intercept(request: HttpRequest, next: HttpHandler): Observable> {
17 | return next.handle(request).pipe(catchError(error => this.errorHandler(error)));
18 | }
19 |
20 | // Customize the default error handler here if needed
21 | private errorHandler(response: HttpEvent): Observable> {
22 | if (!environment.production) {
23 | // Do something with the error
24 | log.error('Request error', response);
25 | }
26 | throw response;
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/app/core/index.ts:
--------------------------------------------------------------------------------
1 | export * from './core.module';
2 | export * from './authentication/authentication.service';
3 | export * from './authentication/authentication.guard';
4 | export * from './http/http.service';
5 | export * from './http/http-cache.service';
6 | export * from './http/api-prefix.interceptor';
7 | export * from './http/cache.interceptor';
8 | export * from './http/error-handler.interceptor';
9 | export * from './route-reusable-strategy';
10 | export * from './logger.service';
11 |
--------------------------------------------------------------------------------
/src/app/core/issue/issue.service.spec.ts:
--------------------------------------------------------------------------------
1 | /* tslint:disable:no-unused-variable */
2 |
3 | import { TestBed, async, inject } from '@angular/core/testing';
4 | import { IssueService } from './issue.service';
5 |
6 | describe('Service: Issue', () => {
7 | beforeEach(() => {
8 | TestBed.configureTestingModule({
9 | providers: [IssueService]
10 | });
11 | });
12 |
13 | it('should ...', inject([IssueService], (service: IssueService) => {
14 | expect(service).toBeTruthy();
15 | }));
16 | });
17 |
--------------------------------------------------------------------------------
/src/app/core/like/like.service.spec.ts:
--------------------------------------------------------------------------------
1 | import { TestBed } from '@angular/core/testing';
2 |
3 | import { LikeService } from './like.service';
4 |
5 | describe('LikeService', () => {
6 | beforeEach(() => TestBed.configureTestingModule({}));
7 |
8 | it('should be created', () => {
9 | const service: LikeService = TestBed.get(LikeService);
10 | expect(service).toBeTruthy();
11 | });
12 | });
13 |
--------------------------------------------------------------------------------
/src/app/core/like/like.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@angular/core';
2 | import { Apollo } from 'apollo-angular';
3 | import { map, take } from 'rxjs/operators';
4 |
5 | @Injectable({
6 | providedIn: 'root'
7 | })
8 | export class LikeService {
9 | constructor(private apollo: Apollo) {}
10 |
11 | /**
12 | * LikeOrDislike
13 | */
14 | public likeOrDisLike(filteredInfo: object) {
15 | const mutation = filteredInfo['mutation'];
16 | delete filteredInfo['mutation'];
17 | return this.apollo
18 | .mutate({
19 | mutation: mutation,
20 | variables: filteredInfo
21 | })
22 | .pipe(take(1))
23 | .pipe(
24 | map((res: any) => {
25 | return res;
26 | })
27 | );
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/app/core/logger.service.spec.ts:
--------------------------------------------------------------------------------
1 | import { Logger, LogLevel, LogOutput } from './logger.service';
2 |
3 | const logMethods = ['log', 'info', 'warn', 'error'];
4 |
5 | describe('Logger', () => {
6 | let savedConsole: Function[];
7 | let savedLevel: LogLevel;
8 | let savedOutputs: LogOutput[];
9 |
10 | beforeAll(() => {
11 | savedConsole = [];
12 | logMethods.forEach(m => {
13 | savedConsole[m] = console[m];
14 | console[m] = () => {};
15 | });
16 | savedLevel = Logger.level;
17 | savedOutputs = Logger.outputs;
18 | });
19 |
20 | afterAll(() => {
21 | logMethods.forEach(m => {
22 | console[m] = savedConsole[m];
23 | });
24 | Logger.level = savedLevel;
25 | Logger.outputs = savedOutputs;
26 | });
27 |
28 | it('should create an instance', () => {
29 | expect(new Logger()).toBeTruthy();
30 | });
31 |
32 | it('should add a new LogOutput and receives log entries', () => {
33 | // Arrange
34 | const outputSpy = jasmine.createSpy('outputSpy');
35 | const log = new Logger('test');
36 |
37 | // Act
38 | Logger.outputs.push(outputSpy);
39 |
40 | log.debug('d');
41 | log.info('i');
42 | log.warn('w');
43 | log.error('e', { error: true });
44 |
45 | // Assert
46 | expect(outputSpy).toHaveBeenCalled();
47 | expect(outputSpy.calls.count()).toBe(4);
48 | expect(outputSpy).toHaveBeenCalledWith('test', LogLevel.Debug, 'd');
49 | expect(outputSpy).toHaveBeenCalledWith('test', LogLevel.Info, 'i');
50 | expect(outputSpy).toHaveBeenCalledWith('test', LogLevel.Warning, 'w');
51 | expect(outputSpy).toHaveBeenCalledWith('test', LogLevel.Error, 'e', { error: true });
52 | });
53 |
54 | it('should add a new LogOutput and receives only production log entries', () => {
55 | // Arrange
56 | const outputSpy = jasmine.createSpy('outputSpy');
57 | const log = new Logger('test');
58 |
59 | // Act
60 | Logger.outputs.push(outputSpy);
61 | Logger.enableProductionMode();
62 |
63 | log.debug('d');
64 | log.info('i');
65 | log.warn('w');
66 | log.error('e', { error: true });
67 |
68 | // Assert
69 | expect(outputSpy).toHaveBeenCalled();
70 | expect(outputSpy.calls.count()).toBe(2);
71 | expect(outputSpy).toHaveBeenCalledWith('test', LogLevel.Warning, 'w');
72 | expect(outputSpy).toHaveBeenCalledWith('test', LogLevel.Error, 'e', { error: true });
73 | });
74 | });
75 |
--------------------------------------------------------------------------------
/src/app/core/profile/profile.service.spec.ts:
--------------------------------------------------------------------------------
1 | import { TestBed } from '@angular/core/testing';
2 |
3 | import { ProfileService } from './profile.service';
4 |
5 | describe('ProfileService', () => {
6 | beforeEach(() => TestBed.configureTestingModule({}));
7 |
8 | it('should be created', () => {
9 | const service: ProfileService = TestBed.get(ProfileService);
10 | expect(service).toBeTruthy();
11 | });
12 | });
13 |
--------------------------------------------------------------------------------
/src/app/core/profile/user.ts:
--------------------------------------------------------------------------------
1 | export class User {
2 | username: string;
3 | full_name: string;
4 | email: string;
5 | is_admin: boolean;
6 | is_staff: boolean;
7 | is_superuser: boolean;
8 | birth_date: Date;
9 | bio: string;
10 | technologies: Array;
11 | social_links: Array;
12 | idea_list: Array