├── .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; 13 | project_list: Array; 14 | 15 | constructor() { 16 | this.username = null; 17 | this.full_name = null; 18 | this.email = null; 19 | this.is_admin = false; 20 | this.is_superuser = null; 21 | this.birth_date = null; 22 | this.bio = null; 23 | this.technologies = null; 24 | this.social_links = null; 25 | this.idea_list = null; 26 | this.project_list = null; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/app/core/project/project.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | 3 | import { ProjectService } from './project.service'; 4 | 5 | describe('ProjectService', () => { 6 | beforeEach(() => TestBed.configureTestingModule({})); 7 | 8 | it('should be created', () => { 9 | const service: ProjectService = TestBed.get(ProjectService); 10 | expect(service).toBeTruthy(); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /src/app/core/review/review.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | 3 | import { ReviewService } from './review.service'; 4 | 5 | describe('ReviewService', () => { 6 | beforeEach(() => TestBed.configureTestingModule({})); 7 | 8 | it('should be created', () => { 9 | const service: ReviewService = TestBed.get(ReviewService); 10 | expect(service).toBeTruthy(); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /src/app/core/route-reusable-strategy.ts: -------------------------------------------------------------------------------- 1 | import { ActivatedRouteSnapshot, DetachedRouteHandle, RouteReuseStrategy } from '@angular/router'; 2 | 3 | /** 4 | * A route strategy allowing for explicit route reuse. 5 | * Used as a workaround for https://github.com/angular/angular/issues/18374 6 | * To reuse a given route, add `data: { reuse: true }` to the route definition. 7 | */ 8 | export class RouteReusableStrategy extends RouteReuseStrategy { 9 | public shouldDetach(route: ActivatedRouteSnapshot): boolean { 10 | return false; 11 | } 12 | 13 | public store(route: ActivatedRouteSnapshot, detachedTree: DetachedRouteHandle | null): void {} 14 | 15 | public shouldAttach(route: ActivatedRouteSnapshot): boolean { 16 | return false; 17 | } 18 | 19 | public retrieve(route: ActivatedRouteSnapshot): DetachedRouteHandle | null { 20 | return null; 21 | } 22 | 23 | public shouldReuseRoute(future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot): boolean { 24 | return future.routeConfig === curr.routeConfig || future.data.reuse; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/app/core/tags/tags.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | 3 | import { TagsService } from './tags.service'; 4 | 5 | describe('TagsService', () => { 6 | beforeEach(() => TestBed.configureTestingModule({})); 7 | 8 | it('should be created', () => { 9 | const service: TagsService = TestBed.get(TagsService); 10 | expect(service).toBeTruthy(); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /src/app/core/tags/tags.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { MUTATION_ADD_TAGS_TO_DB, MUTATION_ADD_TAGS_LINKS } from '@app/shared/mutations/tags-mutations'; 3 | import { Apollo } from 'apollo-angular'; 4 | import { take, map } from 'rxjs/operators'; 5 | import { QUERY_FETCH_TAGS } from '@app/shared/queries/project-queries'; 6 | 7 | @Injectable({ 8 | providedIn: 'root' 9 | }) 10 | export class TagsService { 11 | constructor(private apollo: Apollo) {} 12 | 13 | /** 14 | * Adds Project Tags 15 | */ 16 | public addProjectTags(tags: any, projectId: any) { 17 | const tagTBP: any[] = []; 18 | tags.forEach((tag: any) => { 19 | tagTBP.push({ project_id: projectId, tag_id: tag.tag_id }); 20 | }); 21 | return this.apollo 22 | .mutate({ 23 | mutation: MUTATION_ADD_TAGS_LINKS, 24 | variables: { 25 | objects: tagTBP 26 | } 27 | }) 28 | .pipe(take(1)) 29 | .pipe( 30 | map((resp: any) => { 31 | return resp; 32 | }) 33 | ); 34 | } 35 | 36 | /** 37 | * addNewTagsToDb 38 | */ 39 | public addNewTagsToDb(tagsList: Array) { 40 | return this.apollo 41 | .mutate({ 42 | mutation: MUTATION_ADD_TAGS_TO_DB, 43 | variables: { 44 | objects: tagsList 45 | } 46 | }) 47 | .pipe(take(1)) 48 | .pipe( 49 | map((res: any) => { 50 | return res; 51 | }) 52 | ); 53 | } 54 | 55 | /** 56 | * getTagsFromDB 57 | */ 58 | public getTagsFromDB() { 59 | return this.apollo 60 | .query({ 61 | query: QUERY_FETCH_TAGS 62 | }) 63 | .pipe(take(1)) 64 | .pipe( 65 | map((res: any) => { 66 | return res; 67 | }) 68 | ); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/app/core/user/user.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | 3 | import { UserService } from './user.service'; 4 | 5 | describe('UserService', () => { 6 | beforeEach(() => TestBed.configureTestingModule({})); 7 | 8 | it('should be created', () => { 9 | const service: UserService = TestBed.get(UserService); 10 | expect(service).toBeTruthy(); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /src/app/home/home-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { Routes, RouterModule } from '@angular/router'; 3 | 4 | import { HomeComponent } from './home.component'; 5 | import { Shell } from '@app/shell/shell.service'; 6 | 7 | const routes: Routes = [Shell.childRoutes([{ path: '', component: HomeComponent, data: { title: 'Cynthesize' } }])]; 8 | 9 | @NgModule({ 10 | imports: [RouterModule.forChild(routes)], 11 | exports: [RouterModule], 12 | providers: [] 13 | }) 14 | export class HomeRoutingModule {} 15 | -------------------------------------------------------------------------------- /src/app/home/home.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | import { HttpClientTestingModule } from '@angular/common/http/testing'; 3 | import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; 4 | import { FlexLayoutModule } from '@angular/flex-layout'; 5 | import { RouterTestingModule } from '@angular/router/testing'; 6 | import { Angulartics2Module } from 'angulartics2'; 7 | 8 | import { CoreModule } from '@app/core'; 9 | import { SharedModule } from '@app/shared'; 10 | import { MaterialModule } from '@app/material.module'; 11 | import { HomeComponent } from './home.component'; 12 | 13 | describe('HomeComponent', () => { 14 | let component: HomeComponent; 15 | let fixture: ComponentFixture; 16 | 17 | beforeEach(async(() => { 18 | TestBed.configureTestingModule({ 19 | imports: [ 20 | BrowserAnimationsModule, 21 | FlexLayoutModule, 22 | MaterialModule, 23 | RouterTestingModule, 24 | Angulartics2Module.forRoot(), 25 | CoreModule, 26 | SharedModule, 27 | HttpClientTestingModule 28 | ], 29 | declarations: [HomeComponent], 30 | providers: [] 31 | }).compileComponents(); 32 | })); 33 | 34 | beforeEach(() => { 35 | fixture = TestBed.createComponent(HomeComponent); 36 | component = fixture.componentInstance; 37 | fixture.detectChanges(); 38 | }); 39 | 40 | it('should create', () => { 41 | expect(component).toBeTruthy(); 42 | }); 43 | }); 44 | -------------------------------------------------------------------------------- /src/app/home/home.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import Typed from 'typed.js'; 3 | import { Title } from '@angular/platform-browser'; 4 | 5 | @Component({ 6 | selector: 'app-home', 7 | templateUrl: './home.component.html', 8 | styleUrls: ['./home.component.scss'] 9 | }) 10 | export class HomeComponent implements OnInit { 11 | quote: string; 12 | isLoading: boolean; 13 | username: string = localStorage.getItem('username'); 14 | 15 | constructor(private title: Title) { 16 | this.title.setTitle('Cynthesize'); 17 | } 18 | 19 | ngOnInit() { 20 | this.isLoading = true; 21 | 22 | const options = { 23 | stringsElement: '#typed-strings', 24 | typeSpeed: 30, 25 | backSpeed: 20, 26 | backDelay: 1500, 27 | showCursor: true, 28 | cursorChar: '|', 29 | loop: true 30 | }; 31 | 32 | const typed = new Typed('.typed-element', options); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/app/home/home.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { TranslateModule } from '@ngx-translate/core'; 4 | import { FlexLayoutModule } from '@angular/flex-layout'; 5 | import { Angulartics2Module } from 'angulartics2'; 6 | 7 | import { CoreModule } from '@app/core'; 8 | import { SharedModule } from '@app/shared'; 9 | import { MaterialModule } from '@app/material.module'; 10 | import { HomeRoutingModule } from './home-routing.module'; 11 | import { HomeComponent } from './home.component'; 12 | 13 | @NgModule({ 14 | imports: [ 15 | CommonModule, 16 | TranslateModule, 17 | CoreModule, 18 | SharedModule, 19 | FlexLayoutModule, 20 | MaterialModule, 21 | Angulartics2Module, 22 | HomeRoutingModule 23 | ], 24 | declarations: [HomeComponent], 25 | providers: [] 26 | }) 27 | export class HomeModule {} 28 | -------------------------------------------------------------------------------- /src/app/material.module.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * This module imports and re-exports all Angular Material modules for convenience, 3 | * so only 1 module import is needed in your feature modules. 4 | * See https://material.angular.io/guide/getting-started#step-3-import-the-component-modules. 5 | * 6 | * To optimize your production builds, you should only import the components used in your app. 7 | */ 8 | 9 | import { NgModule } from '@angular/core'; 10 | import { 11 | MatAutocompleteModule, 12 | MatButtonModule, 13 | MatButtonToggleModule, 14 | MatCardModule, 15 | MatCheckboxModule, 16 | MatChipsModule, 17 | MatCommonModule, 18 | MatDatepickerModule, 19 | MatDialogModule, 20 | MatDividerModule, 21 | MatExpansionModule, 22 | MatFormFieldModule, 23 | MatGridListModule, 24 | MatIconModule, 25 | MatInputModule, 26 | MatLineModule, 27 | MatListModule, 28 | MatMenuModule, 29 | MatNativeDateModule, 30 | MatOptionModule, 31 | MatPaginatorModule, 32 | MatProgressBarModule, 33 | MatProgressSpinnerModule, 34 | MatPseudoCheckboxModule, 35 | MatRadioModule, 36 | MatRippleModule, 37 | MatSelectModule, 38 | MatSidenavModule, 39 | MatSliderModule, 40 | MatSlideToggleModule, 41 | MatSnackBarModule, 42 | MatSortModule, 43 | MatStepperModule, 44 | MatTableModule, 45 | MatTabsModule, 46 | MatToolbarModule, 47 | MatTooltipModule 48 | } from '@angular/material'; 49 | 50 | @NgModule({ 51 | exports: [ 52 | MatAutocompleteModule, 53 | MatButtonModule, 54 | MatButtonToggleModule, 55 | MatCardModule, 56 | MatCheckboxModule, 57 | MatChipsModule, 58 | MatCommonModule, 59 | MatDatepickerModule, 60 | MatDialogModule, 61 | MatDividerModule, 62 | MatExpansionModule, 63 | MatFormFieldModule, 64 | MatGridListModule, 65 | MatIconModule, 66 | MatInputModule, 67 | MatLineModule, 68 | MatListModule, 69 | MatMenuModule, 70 | MatNativeDateModule, 71 | MatOptionModule, 72 | MatPaginatorModule, 73 | MatProgressBarModule, 74 | MatProgressSpinnerModule, 75 | MatPseudoCheckboxModule, 76 | MatRadioModule, 77 | MatRippleModule, 78 | MatSelectModule, 79 | MatSidenavModule, 80 | MatSlideToggleModule, 81 | MatSliderModule, 82 | MatSnackBarModule, 83 | MatSortModule, 84 | MatStepperModule, 85 | MatTableModule, 86 | MatTabsModule, 87 | MatToolbarModule, 88 | MatTooltipModule 89 | ] 90 | }) 91 | export class MaterialModule {} 92 | -------------------------------------------------------------------------------- /src/app/profile/profile-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { Routes, RouterModule } from '@angular/router'; 3 | 4 | const routes: Routes = [ 5 | { 6 | path: '', 7 | loadChildren: './user/user.module#UserModule' 8 | }, 9 | { 10 | path: '**', 11 | redirectTo: '' 12 | } 13 | ]; 14 | 15 | @NgModule({ 16 | imports: [RouterModule.forChild(routes)], 17 | exports: [RouterModule] 18 | }) 19 | export class ProfileRoutingModule {} 20 | -------------------------------------------------------------------------------- /src/app/profile/profile.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { ProfileRoutingModule } from './profile-routing.module'; 4 | import { SharedModule } from '@app/shared'; 5 | 6 | @NgModule({ 7 | imports: [CommonModule, ProfileRoutingModule, SharedModule], 8 | declarations: [], 9 | providers: [] 10 | }) 11 | export class ProfileModule {} 12 | -------------------------------------------------------------------------------- /src/app/profile/user/projects/projects.component.html: -------------------------------------------------------------------------------- 1 | 41 | 42 | -------------------------------------------------------------------------------- /src/app/profile/user/projects/projects.component.scss: -------------------------------------------------------------------------------- 1 | .profile-project-page { 2 | background-color: white; 3 | padding: 40px; 4 | margin: 15px auto; 5 | border-radius: 20px; 6 | } 7 | 8 | .profile-heading { 9 | font-size: 1.2rem; 10 | font-weight: 600; 11 | } 12 | .project-icon { 13 | width: 100px; 14 | height: auto; 15 | flex: 1; 16 | border-radius: 6px; 17 | margin-right: 20px; 18 | } 19 | 20 | .launched-project-page-card { 21 | padding: 20px 20px 0 20px; 22 | } 23 | 24 | .tags { 25 | padding: 3px; 26 | border: 1px #dddddd solid; 27 | border-radius: 2px; 28 | font-size: 0.7rem; 29 | margin: 0 5px; 30 | font-weight: 400; 31 | } 32 | -------------------------------------------------------------------------------- /src/app/profile/user/projects/projects.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { ProjectsComponent } from './projects.component'; 4 | 5 | describe('ProjectsComponent', () => { 6 | let component: ProjectsComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ProjectsComponent] 12 | }).compileComponents(); 13 | })); 14 | 15 | beforeEach(() => { 16 | fixture = TestBed.createComponent(ProjectsComponent); 17 | component = fixture.componentInstance; 18 | fixture.detectChanges(); 19 | }); 20 | 21 | it('should create', () => { 22 | expect(component).toBeTruthy(); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /src/app/profile/user/projects/projects.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { ActivatedRoute } from '@angular/router'; 3 | import { ProfileService } from '@app/core/profile/profile.service'; 4 | import { ProjectService } from '@app/core/project/project.service'; 5 | 6 | @Component({ 7 | selector: 'app-projects', 8 | templateUrl: './projects.component.html', 9 | styleUrls: ['./projects.component.scss'] 10 | }) 11 | export class ProjectsComponent implements OnInit { 12 | username: string; 13 | launchedProjects: any[] = []; 14 | isLoading = true; 15 | nullText: string = null; 16 | 17 | constructor( 18 | private route: ActivatedRoute, 19 | private profileService: ProfileService, 20 | public projectService: ProjectService 21 | ) { 22 | this.route.params.subscribe(params => { 23 | this.profileService.getUserProjects(params.username).subscribe(data => { 24 | this.launchedProjects = data.data.user[0].projects; 25 | if (this.launchedProjects.length === 0) { 26 | this.nullText = 'No projects exists.'; 27 | } 28 | this.isLoading = false; 29 | }); 30 | }); 31 | } 32 | 33 | ngOnInit() {} 34 | } 35 | -------------------------------------------------------------------------------- /src/app/profile/user/user-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { Routes, RouterModule } from '@angular/router'; 3 | import { UserComponent } from './user.component'; 4 | 5 | const routes: Routes = [ 6 | { 7 | path: ':username', 8 | component: UserComponent 9 | }, 10 | { 11 | path: '**', 12 | redirectTo: '' 13 | } 14 | ]; 15 | 16 | @NgModule({ 17 | imports: [RouterModule.forChild(routes)], 18 | exports: [RouterModule] 19 | }) 20 | export class UserRoutingModule {} 21 | -------------------------------------------------------------------------------- /src/app/profile/user/user.component.scss: -------------------------------------------------------------------------------- 1 | .link-active { 2 | color: orange; 3 | } 4 | .header-icons { 5 | display: flex; 6 | } 7 | .user-page-icon { 8 | display: flex; 9 | flex-direction: column; 10 | align-items: center; 11 | } 12 | 13 | #project { 14 | margin: 15px; 15 | } 16 | #idea { 17 | margin: 10px; 18 | } 19 | .contribution { 20 | margin: 25px 0 0 25px; 21 | width: 85%; 22 | } 23 | 24 | .info-detail { 25 | display: flex; 26 | align-items: center; 27 | padding: 10px; 28 | font-size: 1rem; 29 | } 30 | .info-detail > mat-icon { 31 | margin-right: 10px; 32 | } 33 | .banner { 34 | width: 100%; 35 | height: 300px; 36 | position: relative; 37 | background-color: orange; 38 | } 39 | .user { 40 | display: flex; 41 | position: absolute; 42 | top: 100px; 43 | left: 10%; 44 | flex-wrap: wrap; 45 | } 46 | .name-edit { 47 | display: flex; 48 | align-items: baseline; 49 | } 50 | .info-bar { 51 | display: flex; 52 | flex-direction: column; 53 | align-items: center; 54 | flex: 1; 55 | } 56 | .image { 57 | border-radius: 50%; 58 | border: 15px white solid; 59 | width: 250px; 60 | height: 260px; 61 | } 62 | .userinfo { 63 | flex: 3; 64 | display: flex; 65 | padding: 125px 0 0 0; 66 | } 67 | .name { 68 | font-weight: 700; 69 | } 70 | @media (max-width: 570px) { 71 | .user { 72 | left: 5%; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/app/profile/user/user.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { UserComponent } from './user.component'; 4 | 5 | describe('UserComponent', () => { 6 | let component: UserComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [UserComponent] 12 | }).compileComponents(); 13 | })); 14 | 15 | beforeEach(() => { 16 | fixture = TestBed.createComponent(UserComponent); 17 | component = fixture.componentInstance; 18 | fixture.detectChanges(); 19 | }); 20 | 21 | it('should create', () => { 22 | expect(component).toBeTruthy(); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /src/app/profile/user/user.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { FormsModule, ReactiveFormsModule } from '@angular/forms'; 4 | import { UserRoutingModule } from './user-routing.module'; 5 | import { SharedModule } from '@app/shared'; 6 | import { MaterialModule } from '@app/material.module'; 7 | import { Cloudinary as CloudinaryCore } from 'cloudinary-core'; 8 | import { CloudinaryConfiguration, CloudinaryModule } from '@cloudinary/angular-5.x'; 9 | import { UserComponent } from './user.component'; 10 | import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material'; 11 | import { ProjectsComponent } from './projects/projects.component'; 12 | 13 | export const cloudinary = { 14 | Cloudinary: CloudinaryCore 15 | }; 16 | export const config: CloudinaryConfiguration = { 17 | cloud_name: 'cynthesize', 18 | upload_preset: 'qdninpjl' 19 | }; 20 | @NgModule({ 21 | imports: [ 22 | CommonModule, 23 | SharedModule, 24 | FormsModule, 25 | MaterialModule, 26 | UserRoutingModule, 27 | ReactiveFormsModule, 28 | CloudinaryModule.forRoot(cloudinary, config) 29 | ], 30 | declarations: [UserComponent, ProjectsComponent], 31 | entryComponents: [], 32 | providers: [ 33 | { 34 | provide: MatDialogRef, 35 | useValue: {} 36 | }, 37 | { 38 | provide: MAT_DIALOG_DATA, 39 | useValue: {} 40 | } 41 | ] 42 | }) 43 | export class UserModule {} 44 | -------------------------------------------------------------------------------- /src/app/shared/activity-actions/activity-actions.component.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | 5 |
6 | -------------------------------------------------------------------------------- /src/app/shared/activity-actions/activity-actions.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cynthesize/cynthesize-frontend/fc71507b4e7bd8a391956a73d4ced3aac87f0b37/src/app/shared/activity-actions/activity-actions.component.scss -------------------------------------------------------------------------------- /src/app/shared/activity-actions/activity-actions.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { ActivityActionsComponent } from './activity-actions.component'; 4 | 5 | describe('ActivityActionsComponent', () => { 6 | let component: ActivityActionsComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ActivityActionsComponent] 12 | }).compileComponents(); 13 | })); 14 | 15 | beforeEach(() => { 16 | fixture = TestBed.createComponent(ActivityActionsComponent); 17 | component = fixture.componentInstance; 18 | fixture.detectChanges(); 19 | }); 20 | 21 | it('should create', () => { 22 | expect(component).toBeTruthy(); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /src/app/shared/activity-actions/activity-actions.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit, Input } from '@angular/core'; 2 | import { MatBottomSheet } from '@angular/material'; 3 | import { ShareSheetComponent } from '../share-sheet/share-sheet.component'; 4 | 5 | @Component({ 6 | selector: 'app-activity-actions', 7 | templateUrl: './activity-actions.component.html', 8 | styleUrls: ['./activity-actions.component.scss'] 9 | }) 10 | export class ActivityActionsComponent implements OnInit { 11 | @Input() likes: any; 12 | @Input() type: any; 13 | @Input() parentProjectId: any; 14 | @Input() id: any; 15 | @Input() url: any; 16 | 17 | constructor(private bottomSheet: MatBottomSheet) {} 18 | 19 | ngOnInit() {} 20 | 21 | openShareSheet(): void { 22 | this.bottomSheet.open(ShareSheetComponent, { 23 | data: { 24 | facebookUrl: 'https://www.facebook.com/sharer/sharer.php?u=https://cynthesize.co' + this.url, 25 | twitterUrl: 'https://twitter.com/home?status=https://cynthesize.co' + this.url, 26 | linkedInUrl: 27 | 'https://www.linkedin.com/shareArticle?mini=true&url=&title=&summary=&source=https://cynthesize.co' + this.url 28 | } 29 | }); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/app/shared/comments/comments.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 | 6 |
7 |
8 | 9 |
10 | Write Comment 11 |
12 |
13 | 14 |
15 | You need to be logged in to comment. 16 |
17 |
18 |
19 | 20 | 21 | 22 |
23 |
24 |
25 | 26 |
27 | 28 | 29 |
30 | 35 |
36 |
37 |
38 |
39 |
40 | -------------------------------------------------------------------------------- /src/app/shared/comments/comments.component.scss: -------------------------------------------------------------------------------- 1 | .comment-box { 2 | background-color: white; 3 | border-radius: 5px; 4 | } 5 | 6 | .comment-container { 7 | padding: 20px; 8 | } 9 | 10 | .add-comment { 11 | background-color: rgb(247, 247, 247); 12 | display: flex; 13 | border-radius: 5px; 14 | align-items: center; 15 | padding: 15px; 16 | } 17 | 18 | cl-image > img { 19 | border-radius: 50%; 20 | } 21 | 22 | .user-icon { 23 | padding-right: 10px; 24 | } 25 | 26 | .init-comment-input { 27 | background-color: white; 28 | border-radius: 5px; 29 | font-size: 0.9rem; 30 | align-items: center; 31 | width: 100%; 32 | display: flex; 33 | padding-left: 30px; 34 | height: 50px; 35 | overflow: visible; 36 | } 37 | 38 | .td-editor-stuff { 39 | width: 100%; 40 | } 41 | 42 | .comment-info { 43 | padding: 15px 0 0 10px; 44 | display: flex; 45 | border-radius: 5px; 46 | align-items: center; 47 | } 48 | 49 | .user-name { 50 | display: flex; 51 | } 52 | 53 | .user-name > a { 54 | margin-right: 5px; 55 | } 56 | 57 | .comment-text-box { 58 | margin-left: 60px; 59 | } 60 | 61 | .comment-text { 62 | font-size: 0.94rem; 63 | line-height: 0.98; 64 | margin-bottom: 10px; 65 | } 66 | 67 | .comment-icons { 68 | display: flex; 69 | align-items: center; 70 | color: grey; 71 | } 72 | 73 | .icon { 74 | color: #2196f3; 75 | margin: 0 5px; 76 | } 77 | 78 | mat-dialog-container { 79 | background-color: rgb(247, 247, 247) !important; 80 | } 81 | 82 | // td-markdown classes 83 | .editor-toolbar { 84 | opacity: 1 !important; 85 | background-color: white !important; 86 | } 87 | -------------------------------------------------------------------------------- /src/app/shared/comments/comments.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { CommentsComponent } from './comments.component'; 4 | 5 | describe('CommentsComponent', () => { 6 | let component: CommentsComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [CommentsComponent] 12 | }).compileComponents(); 13 | })); 14 | 15 | beforeEach(() => { 16 | fixture = TestBed.createComponent(CommentsComponent); 17 | component = fixture.componentInstance; 18 | fixture.detectChanges(); 19 | }); 20 | 21 | it('should create', () => { 22 | expect(component).toBeTruthy(); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /src/app/shared/comments/comments.component.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Component, 3 | OnInit, 4 | Input, 5 | EventEmitter, 6 | Output, 7 | ViewChild, 8 | ViewEncapsulation, 9 | SimpleChanges, 10 | OnChanges 11 | } from '@angular/core'; 12 | import { Router } from '@angular/router'; 13 | import { ErrorHandlerService } from '@app/core/error-handler.service'; 14 | import { CommentsService } from '@app/core/comments/comments.service'; 15 | import { AuthenticationService } from '@app/core'; 16 | import { LoginComponent } from '@app/auth/login/login.component'; 17 | import { MatDialog } from '@angular/material'; 18 | 19 | @Component({ 20 | selector: 'app-comments', 21 | templateUrl: './comments.component.html', 22 | styleUrls: ['./comments.component.scss'], 23 | encapsulation: ViewEncapsulation.None 24 | }) 25 | export class CommentsComponent implements OnInit { 26 | @Input() activityType: string; 27 | @Input() activityId: number; 28 | commentObject: any[] = []; 29 | user_pic = localStorage.getItem('user_profile_pic'); 30 | commentText = ''; 31 | 32 | isCommenting = false; 33 | options: any = { 34 | lineWrapping: true 35 | }; 36 | 37 | constructor( 38 | private commentService: CommentsService, 39 | private errorHandler: ErrorHandlerService, 40 | public authenticationService: AuthenticationService, 41 | private dialog: MatDialog 42 | ) {} 43 | 44 | ngOnInit(): void { 45 | this.fetchComments(); 46 | } 47 | 48 | fetchComments(): void { 49 | this.commentService.fetchComments(this.activityId, this.activityType).subscribe( 50 | (data: any) => { 51 | this.commentObject = data.data.comment; 52 | }, 53 | (error: any) => { 54 | this.errorHandler.subj_notification.next(error); 55 | } 56 | ); 57 | } 58 | 59 | addComment() { 60 | this.commentService.addComment(this.activityId, this.activityType, this.commentText).subscribe( 61 | comment => { 62 | this.commentText = ''; 63 | this.commentObject.push(comment.data.insert_comment.returning[0]); 64 | this.isCommenting = false; 65 | }, 66 | error => { 67 | this.errorHandler.subj_notification.next(error); 68 | } 69 | ); 70 | } 71 | 72 | openLoginDialog(): void { 73 | this.dialog.open(LoginComponent, { 74 | width: 'auto' 75 | }); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/app/shared/comments/editable-comment/editable-comment.component.scss: -------------------------------------------------------------------------------- 1 | .mat-menu-content { 2 | display: flex; 3 | flex-direction: column; 4 | align-items: center; 5 | color: black; 6 | overflow-x: hidden; 7 | } 8 | -------------------------------------------------------------------------------- /src/app/shared/comments/editable-comment/editable-comment.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { EditableCommentComponent } from './editable-comment.component'; 4 | 5 | describe('EditableCommentComponent', () => { 6 | let component: EditableCommentComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [EditableCommentComponent] 12 | }).compileComponents(); 13 | })); 14 | 15 | beforeEach(() => { 16 | fixture = TestBed.createComponent(EditableCommentComponent); 17 | component = fixture.componentInstance; 18 | fixture.detectChanges(); 19 | }); 20 | 21 | it('should create', () => { 22 | expect(component).toBeTruthy(); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /src/app/shared/comments/editable-comment/editable-comment.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit, Input, Output, ViewEncapsulation, EventEmitter } from '@angular/core'; 2 | import { ProjectService } from '@app/core/project/project.service'; 3 | import { finalize } from 'rxjs/operators'; 4 | import { ErrorHandlerService } from '@app/core/error-handler.service'; 5 | import { CommentsService } from '@app/core/comments/comments.service'; 6 | 7 | @Component({ 8 | selector: 'app-editable-comment', 9 | templateUrl: './editable-comment.component.html', 10 | styleUrls: ['./editable-comment.component.scss'], 11 | encapsulation: ViewEncapsulation.None 12 | }) 13 | export class EditableCommentComponent implements OnInit { 14 | @Input() comment: any; 15 | @Input() reply: any; 16 | @Input() correspondingComment: any; 17 | @Input() activityId: number; 18 | // If a comment was edited this event will be emitted 19 | @Output() commentEdited = new EventEmitter(); 20 | replyText = ''; 21 | 22 | options: any = { 23 | lineWrapping: true 24 | }; 25 | isReplying = false; 26 | 27 | constructor( 28 | public projectService: ProjectService, 29 | private errorHandler: ErrorHandlerService, 30 | private commentService: CommentsService 31 | ) {} 32 | 33 | ngOnInit() {} 34 | 35 | onContentSaved(content: any) { 36 | this.commentEdited.next(content); 37 | } 38 | 39 | addReply() { 40 | let comment_id: number; 41 | if (this.reply) { 42 | comment_id = this.reply.comment_id; 43 | } else { 44 | comment_id = this.comment.id; 45 | } 46 | this.commentService.addReplyToComment(this.replyText, comment_id).subscribe( 47 | reply => { 48 | if (this.reply) { 49 | this.correspondingComment.replies.push(reply.data.insert_reply.returning[0]); 50 | } else { 51 | this.comment.replies.push(reply.data.insert_reply.returning[0]); 52 | } 53 | this.replyText = ''; 54 | this.isReplying = false; 55 | }, 56 | error => { 57 | this.errorHandler.subj_notification.next(error); 58 | } 59 | ); 60 | } 61 | 62 | reportComment() { 63 | this.commentService.reportAComment(this.comment.id).subscribe( 64 | data => { 65 | this.errorHandler.subj_notification.next('This comment has reported! We will look into it.'); 66 | }, 67 | error => { 68 | this.errorHandler.subj_notification.next(error); 69 | } 70 | ); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/app/shared/fragments/project-fragments.ts: -------------------------------------------------------------------------------- 1 | import gql from 'graphql-tag'; 2 | 3 | const ISSUE_COMMENT_REPLY_FRAGMENT = gql` 4 | fragment IssueCommentReplyFragment on reply { 5 | id 6 | reply_text 7 | user { 8 | id 9 | username 10 | profile_pic 11 | } 12 | previous_edits 13 | timestamp 14 | } 15 | `; 16 | 17 | const ISSUE_COMMENT_FRAGMENT = gql` 18 | fragment IssueCommentFragment on comment { 19 | id 20 | comment_text 21 | user { 22 | id 23 | username 24 | profile_pic 25 | } 26 | issue_id 27 | likes 28 | timestamp 29 | replies { 30 | ...IssueCommentReplyFragment 31 | } 32 | } 33 | ${ISSUE_COMMENT_REPLY_FRAGMENT} 34 | `; 35 | 36 | const PROJECT_ISSUE_FRAGMENT = gql` 37 | fragment ProjectIssueFragment on issues { 38 | id 39 | description 40 | checkpoint_name 41 | created_on 42 | user { 43 | id 44 | username 45 | name 46 | profile_pic 47 | is_mentor 48 | } 49 | is_resolved 50 | } 51 | `; 52 | 53 | const LAUNCHED_PROJECT_DETAILS_FRAGMENT = gql` 54 | fragment LaunchedProjectDetailsFragment on projects { 55 | id 56 | project_name 57 | created_on 58 | current_stage 59 | tech_stack 60 | website 61 | roles_opened 62 | is_public 63 | abstract 64 | icon 65 | likes 66 | tagsLinkssByprojectId { 67 | tagsBytagId { 68 | tag_id 69 | tag_name 70 | } 71 | } 72 | user { 73 | id 74 | username 75 | profile_pic 76 | } 77 | } 78 | `; 79 | 80 | export { 81 | ISSUE_COMMENT_FRAGMENT, 82 | ISSUE_COMMENT_REPLY_FRAGMENT, 83 | LAUNCHED_PROJECT_DETAILS_FRAGMENT, 84 | PROJECT_ISSUE_FRAGMENT 85 | }; 86 | -------------------------------------------------------------------------------- /src/app/shared/fragments/user-fragments.ts: -------------------------------------------------------------------------------- 1 | import gql from 'graphql-tag'; 2 | 3 | const USER_PROFILE_PIC_FRAGMENT = gql` 4 | fragment UserProfilePicFragment on user { 5 | id 6 | profile_pic 7 | username 8 | is_mentor 9 | } 10 | `; 11 | 12 | const USER_LIKES_FRAGMENT = gql` 13 | fragment UserLikesFragment on user { 14 | id 15 | comment_likes { 16 | comment_id 17 | } 18 | project_likes { 19 | project_id 20 | } 21 | reply_likes { 22 | reply_id 23 | } 24 | } 25 | `; 26 | 27 | const LAUNCHED_USER_PROJECT_DETAILS_FRAGMENT = gql` 28 | fragment LaunchedUserProjectDetailsFragment on user { 29 | id 30 | projects { 31 | id 32 | project_name 33 | created_on 34 | current_stage 35 | tech_stack 36 | website 37 | roles_opened 38 | is_public 39 | abstract 40 | icon 41 | likes 42 | tagsLinkssByprojectId { 43 | tagsBytagId { 44 | tag_name 45 | } 46 | } 47 | } 48 | } 49 | `; 50 | 51 | const USER_DETAILS_FRAGMENT = gql` 52 | fragment UserDetailsFragment on user { 53 | ...UserProfilePicFragment 54 | bio 55 | email 56 | location 57 | name 58 | date_of_birth 59 | social_links 60 | technologies 61 | website 62 | } 63 | ${USER_PROFILE_PIC_FRAGMENT} 64 | `; 65 | 66 | export { 67 | USER_PROFILE_PIC_FRAGMENT, 68 | USER_DETAILS_FRAGMENT, 69 | USER_LIKES_FRAGMENT, 70 | LAUNCHED_USER_PROJECT_DETAILS_FRAGMENT 71 | }; 72 | -------------------------------------------------------------------------------- /src/app/shared/index.ts: -------------------------------------------------------------------------------- 1 | export * from './shared.module'; 2 | -------------------------------------------------------------------------------- /src/app/shared/like/like.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 13 | 14 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /src/app/shared/like/like.component.scss: -------------------------------------------------------------------------------- 1 | .liked { 2 | color: red; 3 | } 4 | .icon { 5 | display: flex; 6 | align-items: center; 7 | } 8 | -------------------------------------------------------------------------------- /src/app/shared/like/like.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { LikeComponent } from './like.component'; 4 | 5 | describe('LikeComponent', () => { 6 | let component: LikeComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [LikeComponent] 12 | }).compileComponents(); 13 | })); 14 | 15 | beforeEach(() => { 16 | fixture = TestBed.createComponent(LikeComponent); 17 | component = fixture.componentInstance; 18 | fixture.detectChanges(); 19 | }); 20 | 21 | it('should create', () => { 22 | expect(component).toBeTruthy(); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /src/app/shared/mutations/tags-mutations.ts: -------------------------------------------------------------------------------- 1 | import gql from 'graphql-tag'; 2 | 3 | const MUTATION_ADD_TAGS_LINKS = gql` 4 | mutation insert_tag_links($objects: [tags_links_insert_input!]!) { 5 | insert_tags_links(objects: $objects) { 6 | affected_rows 7 | } 8 | } 9 | `; 10 | 11 | const MUTATION_ADD_TAGS_TO_DB = gql` 12 | mutation insert_tags_to_db($objects: [tags_insert_input!]!) { 13 | insert_tags(objects: $objects) { 14 | affected_rows 15 | returning { 16 | tag_id 17 | tag_name 18 | } 19 | } 20 | } 21 | `; 22 | 23 | export { MUTATION_ADD_TAGS_LINKS, MUTATION_ADD_TAGS_TO_DB }; 24 | -------------------------------------------------------------------------------- /src/app/shared/not-found/not-found.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-not-found', 5 | template: ` 6 |
7 | 8 |

9 | We looked for it, but couldn't find the page for you. Go back to exploring 10 | Cynthesize 11 |

12 |
13 | `, 14 | styles: [ 15 | ` 16 | .not-found { 17 | display: flex; 18 | align-items: center; 19 | justify-content: center; 20 | flex-direction: column; 21 | height: 100%; 22 | } 23 | .not-found p { 24 | color: black; 25 | font-size: 1rem; 26 | text-align: center; 27 | } 28 | @media (max-width: 650px) { 29 | .not-found img { 30 | width: 100%; 31 | } 32 | .not-found { 33 | width: 90%; 34 | margin: 0 auto; 35 | } 36 | } 37 | ` 38 | ] 39 | }) 40 | export class NotFoundComponent implements OnInit { 41 | constructor() {} 42 | 43 | ngOnInit() {} 44 | } 45 | -------------------------------------------------------------------------------- /src/app/shared/objects.ts: -------------------------------------------------------------------------------- 1 | export interface Project { 2 | area_of_issues_open: [ 3 | { 4 | Design: Array; 5 | Funding: Array; 6 | Ideation: Array; 7 | MVP: Array; 8 | Market: Array; 9 | Miscellaneous: Array; 10 | 'Product Domain': Array; 11 | Security: Array; 12 | Team: Array; 13 | 'Tech Stack': Array; 14 | } 15 | ]; 16 | collaborators: Array; 17 | created_on: string; 18 | current_stage: string; 19 | description: string; 20 | endorsements: Array; 21 | id: number; 22 | owner: string; 23 | project_id: string; 24 | project_name: string; 25 | timestamp: Date; 26 | watching: Array; 27 | } 28 | 29 | export interface IssueCommentsReplies { 30 | id: number; 31 | reply_text: string; 32 | respondent: string; 33 | comment_id: number; 34 | likes: number; 35 | previous_edits: Array; 36 | timestamp: Date; 37 | } 38 | 39 | export interface IssueComments { 40 | id: number; 41 | comment_text: string; 42 | commenter: string; 43 | likes: number; 44 | comment_replies: Array; 45 | issue_id: number; 46 | previous_edits: Array; 47 | project_id: number; 48 | timestamp: Date; 49 | } 50 | 51 | export interface Issue { 52 | checkpoint_name: string; 53 | comments: Array; 54 | created_by: number; 55 | created_on: string; 56 | description: string; 57 | is_resolved: boolean; 58 | project_id: number; 59 | } 60 | -------------------------------------------------------------------------------- /src/app/shared/pipes/time-diff.pipe.spec.ts: -------------------------------------------------------------------------------- 1 | import { TimeDiffPipe } from './time-diff.pipe'; 2 | 3 | describe('TimeDiffPipe', () => { 4 | it('create an instance', () => { 5 | const pipe = new TimeDiffPipe(); 6 | expect(pipe).toBeTruthy(); 7 | }); 8 | }); 9 | -------------------------------------------------------------------------------- /src/app/shared/queries/user-queries.ts: -------------------------------------------------------------------------------- 1 | import gql from 'graphql-tag'; 2 | import { 3 | USER_PROFILE_PIC_FRAGMENT, 4 | USER_DETAILS_FRAGMENT, 5 | USER_LIKES_FRAGMENT, 6 | LAUNCHED_USER_PROJECT_DETAILS_FRAGMENT 7 | } from '../fragments/user-fragments'; 8 | 9 | const QUERY_USER_CHECK = gql` 10 | query fetch_user($email: String!) { 11 | user(where: { email: { _eq: $email } }) { 12 | ...UserProfilePicFragment 13 | is_mentor 14 | } 15 | } 16 | ${USER_PROFILE_PIC_FRAGMENT} 17 | `; 18 | 19 | const QUERY_USER_DETAILS = gql` 20 | query fetch_user($username: String!) { 21 | user(where: { username: { _eq: $username } }) { 22 | ...UserDetailsFragment 23 | } 24 | } 25 | ${USER_DETAILS_FRAGMENT} 26 | `; 27 | 28 | const QUERY_USER_LIKES = gql` 29 | query users_likes($userName: String!) { 30 | user(where: { username: { _eq: $userName } }) { 31 | ...UserLikesFragment 32 | } 33 | } 34 | ${USER_LIKES_FRAGMENT} 35 | `; 36 | 37 | const QUERY_PROJECTS_BY_USER = gql` 38 | query fetch_newest_projects($username: String!) { 39 | user(where: { username: { _eq: $username } }) { 40 | ...LaunchedUserProjectDetailsFragment 41 | } 42 | } 43 | ${LAUNCHED_USER_PROJECT_DETAILS_FRAGMENT} 44 | `; 45 | 46 | const QUERY_APPLIED_FOR_MENTORSHIP = gql` 47 | query applied_for_mentorship($userId: String!) { 48 | mentor_data(where: { user_id: { _eq: $userId } }) { 49 | id 50 | user { 51 | is_mentor 52 | } 53 | } 54 | } 55 | `; 56 | 57 | const QUERY_IS_MENTOR = gql` 58 | query is_mentor($userId: String!) { 59 | user(where: { user_id: { _eq: $userId } }) { 60 | id 61 | is_mentor 62 | } 63 | } 64 | `; 65 | 66 | export { 67 | QUERY_USER_CHECK, 68 | QUERY_USER_DETAILS, 69 | QUERY_USER_LIKES, 70 | QUERY_PROJECTS_BY_USER, 71 | QUERY_APPLIED_FOR_MENTORSHIP, 72 | QUERY_IS_MENTOR 73 | }; 74 | -------------------------------------------------------------------------------- /src/app/shared/share-sheet/share-sheet.component.html: -------------------------------------------------------------------------------- 1 | 2 | Share on Facebook 3 | 4 | Share on Twitter 5 | 6 | Share on LinkedIn 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/app/shared/share-sheet/share-sheet.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cynthesize/cynthesize-frontend/fc71507b4e7bd8a391956a73d4ced3aac87f0b37/src/app/shared/share-sheet/share-sheet.component.scss -------------------------------------------------------------------------------- /src/app/shared/share-sheet/share-sheet.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { ShareSheetComponent } from './share-sheet.component'; 4 | 5 | describe('ShareSheetComponent', () => { 6 | let component: ShareSheetComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ShareSheetComponent] 12 | }).compileComponents(); 13 | })); 14 | 15 | beforeEach(() => { 16 | fixture = TestBed.createComponent(ShareSheetComponent); 17 | component = fixture.componentInstance; 18 | fixture.detectChanges(); 19 | }); 20 | 21 | it('should create', () => { 22 | expect(component).toBeTruthy(); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /src/app/shared/share-sheet/share-sheet.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit, Input, Inject } from '@angular/core'; 2 | import { MatBottomSheetRef, MAT_BOTTOM_SHEET_DATA } from '@angular/material'; 3 | 4 | @Component({ 5 | selector: 'app-share-sheet', 6 | templateUrl: './share-sheet.component.html', 7 | styleUrls: ['./share-sheet.component.scss'] 8 | }) 9 | export class ShareSheetComponent implements OnInit { 10 | facebookUrl: string; 11 | twitterUrl: string; 12 | linkedInUrl: string; 13 | 14 | constructor( 15 | private bottomSheetRef: MatBottomSheetRef, 16 | @Inject(MAT_BOTTOM_SHEET_DATA) public data: any 17 | ) { 18 | this.facebookUrl = data.facebookUrl || null; 19 | this.twitterUrl = data.twitterUrl || null; 20 | this.linkedInUrl = data.linkedInUrl || null; 21 | } 22 | 23 | openLink(event: MouseEvent): void { 24 | this.bottomSheetRef.dismiss(); 25 | event.preventDefault(); 26 | } 27 | ngOnInit() {} 28 | } 29 | -------------------------------------------------------------------------------- /src/app/shared/unauthorized/unauthorized.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-not-found', 5 | template: ` 6 |
7 | 8 |

9 | Oops! You tried accessing something you shouldn't be. Try logging in or go back to exploring 10 | Cynthesize 11 |

12 |
13 | `, 14 | styles: [ 15 | ` 16 | .not-found { 17 | display: flex; 18 | align-items: center; 19 | justify-content: center; 20 | flex-direction: column; 21 | height: 100%; 22 | } 23 | .not-found p { 24 | color: black; 25 | font-size: 1rem; 26 | text-align: center; 27 | } 28 | .not-found p a { 29 | color: black; 30 | } 31 | @media (max-width: 650px) { 32 | .not-found img { 33 | width: 100%; 34 | } 35 | .not-found { 36 | width: 90%; 37 | margin: 0 auto; 38 | } 39 | } 40 | ` 41 | ] 42 | }) 43 | export class UnauthorisedComponent implements OnInit { 44 | constructor() {} 45 | 46 | ngOnInit() {} 47 | } 48 | -------------------------------------------------------------------------------- /src/app/shared/user-name/user-name.component.html: -------------------------------------------------------------------------------- 1 |
2 | {{ time | timeDiff }} by {{ username }} 5 |
6 | -------------------------------------------------------------------------------- /src/app/shared/user-name/user-name.component.scss: -------------------------------------------------------------------------------- 1 | .username-time { 2 | opacity: 0.4; 3 | font-size: smaller; 4 | padding: 0 0 5px 0; 5 | } 6 | -------------------------------------------------------------------------------- /src/app/shared/user-name/user-name.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { UserNameComponent } from './user-name.component'; 4 | 5 | describe('UserNameComponent', () => { 6 | let component: UserNameComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [UserNameComponent] 12 | }).compileComponents(); 13 | })); 14 | 15 | beforeEach(() => { 16 | fixture = TestBed.createComponent(UserNameComponent); 17 | component = fixture.componentInstance; 18 | fixture.detectChanges(); 19 | }); 20 | 21 | it('should create', () => { 22 | expect(component).toBeTruthy(); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /src/app/shared/user-name/user-name.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit, Input } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-user-name', 5 | templateUrl: './user-name.component.html', 6 | styleUrls: ['./user-name.component.scss'] 7 | }) 8 | export class UserNameComponent implements OnInit { 9 | @Input() username: string; 10 | @Input() time: string; 11 | @Input() isMentor: boolean; 12 | 13 | constructor() {} 14 | 15 | ngOnInit() {} 16 | } 17 | -------------------------------------------------------------------------------- /src/app/shell/graphql/graphql.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { HttpClientModule, HttpHeaders } from '@angular/common/http'; 3 | import { Apollo, ApolloModule } from 'apollo-angular'; 4 | import { HttpLink, HttpLinkModule } from 'apollo-angular-link-http'; 5 | import { InMemoryCache } from 'apollo-cache-inmemory'; 6 | import { GRAPHQL_URL } from '../../../environments/environment'; 7 | import { AuthenticationService } from '@app/core'; 8 | 9 | @NgModule({ 10 | exports: [HttpClientModule, ApolloModule, HttpLinkModule] 11 | }) 12 | export class GraphqlModule { 13 | constructor(apollo: Apollo, httpLink: HttpLink, private authService: AuthenticationService) { 14 | // Replace this URL with your deployed endpoint of Hasura on Heroku. 15 | const uri = GRAPHQL_URL; 16 | 17 | /** Following values need to be added to the header before making any 18 | * query. 19 | * 1. X-Hasura-Access-Key: This Access Key is what will let your app access your endpoint. 20 | * 2. Content-Type: To tell the type of content. 21 | * 3. Authorization: This is the token that tells that a user is logged in. 22 | * 4. X-Hasura-Role: This will be 'user' to let Hasura know that a user is accessing the endpoint. 23 | * 5. X-Hasura-User-Id: This the user id of the user. 24 | */ 25 | let authHeader: any; 26 | 27 | if (this.authService.user_id) { 28 | authHeader = new HttpHeaders() 29 | .set('Content-Type', 'application/json') 30 | .set('Authorization', `Bearer ${this.authService.idToken}`); 31 | } else { 32 | authHeader = new HttpHeaders().set('Content-Type', 'application/json'); 33 | } 34 | 35 | // Create a HTTP Link with the URI and the header. 36 | const http = httpLink.create({ uri, headers: authHeader }); 37 | 38 | // Create an Apollo client with HTTP Link and cache as InMemoryCache. 39 | apollo.create({ 40 | link: http, 41 | cache: new InMemoryCache() 42 | }); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/app/shell/header/header.component.scss: -------------------------------------------------------------------------------- 1 | @import "src/theme/theme-variables"; 2 | @import "../../../sass/declarations"; 3 | 4 | .navbar { 5 | position: fixed; 6 | top: 0; 7 | left: 0; 8 | right: 0; 9 | z-index: 1; 10 | border: 1px #dddddd solid; 11 | } 12 | 13 | .mat-button { 14 | font-size: 16px; 15 | } 16 | 17 | .mat-elevation-z1 { 18 | box-shadow: 0px 0px 0px 0px rgba(0, 0, 0, 0), 0px 0px 0px 0px rgba(0, 0, 0, 0), 0px 0px 0px 0px rgba(0, 0, 0, 0); 19 | } 20 | 21 | .header { 22 | font-size: 16px; 23 | color: #2196f3; 24 | 25 | background-color: rgb(255, 255, 255); 26 | 27 | &__full-logo { 28 | padding-top: 1rem; 29 | margin-right: 2rem; 30 | text-decoration: none; 31 | } 32 | } 33 | 34 | .container { 35 | width: 75%; 36 | margin: 0 auto; 37 | } 38 | 39 | @media (max-width: 550px) { 40 | .container { 41 | width: 90%; 42 | } 43 | } 44 | 45 | .mat-toolbar-single-row { 46 | height: 68px !important; 47 | } 48 | 49 | .user-info { 50 | display: flex; 51 | padding: 10px 20px; 52 | align-items: center; 53 | } 54 | 55 | .username { 56 | color: black; 57 | padding: 15px; 58 | font-weight: 600; 59 | font-size: 1.2rem; 60 | } 61 | 62 | .user-link { 63 | padding: 1rem; 64 | font-size: 1rem; 65 | color: $color-primary-dark; 66 | cursor: pointer; 67 | 68 | &:nth-child(odd) { 69 | background-color: darken($color: #ffffff, $amount: 3%); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/app/shell/header/header.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 | 5 | import { MaterialModule } from '@app/material.module'; 6 | import { AuthenticationService } from '@app/core'; 7 | import { HeaderComponent } from './header.component'; 8 | 9 | describe('HeaderComponent', () => { 10 | let component: HeaderComponent; 11 | let fixture: ComponentFixture; 12 | 13 | beforeEach(async(() => { 14 | TestBed.configureTestingModule({ 15 | imports: [RouterTestingModule, MaterialModule, TranslateModule.forRoot()], 16 | declarations: [HeaderComponent], 17 | providers: [{ provide: AuthenticationService }] 18 | }).compileComponents(); 19 | })); 20 | 21 | beforeEach(() => { 22 | fixture = TestBed.createComponent(HeaderComponent); 23 | component = fixture.componentInstance; 24 | fixture.detectChanges(); 25 | }); 26 | 27 | it('should create', () => { 28 | expect(component).toBeTruthy(); 29 | }); 30 | }); 31 | -------------------------------------------------------------------------------- /src/app/shell/header/header.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit, Input, ViewEncapsulation } from '@angular/core'; 2 | import { MatSidenav } from '@angular/material'; 3 | import { AuthenticationService } from '@app/core'; 4 | import { ProfileService } from '@app/core/profile/profile.service'; 5 | 6 | @Component({ 7 | selector: 'app-header', 8 | templateUrl: './header.component.html', 9 | styleUrls: ['./header.component.scss'], 10 | encapsulation: ViewEncapsulation.None 11 | }) 12 | export class HeaderComponent implements OnInit { 13 | @Input() 14 | sidenav: MatSidenav; 15 | isMentor = JSON.parse(localStorage.getItem('is_mentor')); 16 | 17 | constructor( 18 | public authenticationService: AuthenticationService, 19 | private profileService: ProfileService, 20 | private authService: AuthenticationService 21 | ) { 22 | if (this.authService.isAuthenticated()) { 23 | this.profileService.checkIfUserIsMentor().subscribe( 24 | data => { 25 | this.isMentor = data.user[0].is_mentor; 26 | localStorage.setItem('is_mentor', this.isMentor); 27 | }, 28 | error => {} 29 | ); 30 | } 31 | } 32 | 33 | ngOnInit() {} 34 | 35 | get username(): string { 36 | return localStorage.getItem('username'); 37 | } 38 | get profilepPic(): string { 39 | return localStorage.getItem('user_profile_pic'); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/app/shell/header/requests/requests.component.html: -------------------------------------------------------------------------------- 1 |
2 |

Got a suggestion?

3 |

Inform us and help us improve mood

4 |
5 |
6 | Is this a bug? Select "No", if you want a new feature ;) 7 |
8 | 9 | Yes 10 | No 11 | 12 |
13 | 14 | Let us know who's asking 15 | 16 | 17 | 18 | Type your request 19 | 20 | 21 |
22 | 23 |
24 |
25 | -------------------------------------------------------------------------------- /src/app/shell/header/requests/requests.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cynthesize/cynthesize-frontend/fc71507b4e7bd8a391956a73d4ced3aac87f0b37/src/app/shell/header/requests/requests.component.scss -------------------------------------------------------------------------------- /src/app/shell/header/requests/requests.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit, Inject } from '@angular/core'; 2 | import { FormBuilder, Validators, FormGroup } from '@angular/forms'; 3 | import { UserService } from '@app/core/user/user.service'; 4 | import { ErrorHandlerService } from '@app/core/error-handler.service'; 5 | import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material'; 6 | 7 | @Component({ 8 | selector: 'app-requests', 9 | templateUrl: './requests.component.html', 10 | styleUrls: ['./requests.component.scss'] 11 | }) 12 | export class RequestsComponent implements OnInit { 13 | reqForm: FormGroup; 14 | 15 | constructor( 16 | private fb: FormBuilder, 17 | private userService: UserService, 18 | private errorHandler: ErrorHandlerService, 19 | public dialogRef: MatDialogRef, 20 | @Inject(MAT_DIALOG_DATA) public data: any 21 | ) {} 22 | 23 | ngOnInit() { 24 | this.reqForm = this.fb.group({ 25 | is_bug: [false, Validators.required], 26 | text: ['', Validators.required], 27 | name: ['Anonymous', Validators.required] 28 | }); 29 | } 30 | 31 | submitRequest() { 32 | this.userService.sendReq(this.reqForm.value).subscribe( 33 | (data: any) => { 34 | this.dialogRef.close(); 35 | this.errorHandler.subj_notification.next('Request Sent!'); 36 | }, 37 | (error: any) => { 38 | this.errorHandler.subj_notification.next(error); 39 | } 40 | ); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/app/shell/shell.component.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | 29 | 30 | 31 | 36 | 37 |
38 | 39 |
40 |
41 |
42 |
43 | -------------------------------------------------------------------------------- /src/app/shell/shell.component.scss: -------------------------------------------------------------------------------- 1 | @import "src/theme/theme-variables"; 2 | 3 | :host { 4 | display: flex; 5 | flex: 1; 6 | } 7 | 8 | .mat-sidenav { 9 | min-width: 270px; 10 | max-width: 28%; 11 | } 12 | 13 | .has-border { 14 | border-right: 1px solid rgba(0, 0, 0, 0.12); 15 | } 16 | 17 | .mat-list { 18 | padding-top: 0; 19 | } 20 | 21 | .mat-list a { 22 | font-weight: 600; 23 | } 24 | 25 | .active-content { 26 | color: #2196f3; 27 | } 28 | 29 | .mat-list-item { 30 | text-decoration: none; 31 | 32 | &:hover { 33 | background: rgba(0, 0, 0, 0.05); 34 | } 35 | } 36 | 37 | .mat-sidenav, 38 | .mat-drawer-content, 39 | // Force style down to child components 40 | // See https://angular.io/guide/component-styles#deep 41 | :host ::ng-deep .mat-drawer-backdrop { 42 | top: 64px; 43 | bottom: 0; 44 | left: 0; 45 | right: 0; 46 | height: auto; 47 | position: absolute; 48 | } 49 | 50 | @media ($mat-xsmall) { 51 | .mat-sidenav, 52 | .mat-drawer-content, 53 | :host ::ng-deep .mat-drawer-backdrop { 54 | top: 56px; 55 | } 56 | } 57 | 58 | .feedback-icon { 59 | position: fixed; 60 | border-radius: 0 13px 13px 0; 61 | top: 150px; 62 | background: white; 63 | color: #2196f3; 64 | } 65 | -------------------------------------------------------------------------------- /src/app/shell/shell.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | import { RouterTestingModule } from '@angular/router/testing'; 3 | import { TranslateModule } from '@ngx-translate/core'; 4 | import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; 5 | import { FlexLayoutModule } from '@angular/flex-layout'; 6 | import { MaterialModule } from '@app/material.module'; 7 | 8 | import { AuthenticationService, CoreModule } from '@app/core'; 9 | 10 | import { ShellComponent } from './shell.component'; 11 | import { HeaderComponent } from './header/header.component'; 12 | 13 | describe('ShellComponent', () => { 14 | let component: ShellComponent; 15 | let fixture: ComponentFixture; 16 | 17 | beforeEach(async(() => { 18 | TestBed.configureTestingModule({ 19 | imports: [ 20 | RouterTestingModule, 21 | TranslateModule.forRoot(), 22 | BrowserAnimationsModule, 23 | FlexLayoutModule, 24 | MaterialModule, 25 | CoreModule 26 | ], 27 | providers: [{ provide: AuthenticationService }], 28 | declarations: [HeaderComponent, ShellComponent] 29 | }).compileComponents(); 30 | })); 31 | 32 | beforeEach(() => { 33 | fixture = TestBed.createComponent(ShellComponent); 34 | component = fixture.componentInstance; 35 | fixture.detectChanges(); 36 | }); 37 | 38 | it('should create', () => { 39 | expect(component).toBeTruthy(); 40 | }); 41 | }); 42 | -------------------------------------------------------------------------------- /src/app/shell/shell.module.spec.ts: -------------------------------------------------------------------------------- 1 | import { ShellModule } from './shell.module'; 2 | 3 | describe('ShellModule', () => { 4 | let shellModule: ShellModule; 5 | 6 | beforeEach(() => { 7 | shellModule = new ShellModule(); 8 | }); 9 | 10 | it('should create an instance', () => { 11 | expect(shellModule).toBeTruthy(); 12 | }); 13 | }); 14 | -------------------------------------------------------------------------------- /src/app/shell/shell.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { TranslateModule } from '@ngx-translate/core'; 4 | import { RouterModule } from '@angular/router'; 5 | import { FlexLayoutModule } from '@angular/flex-layout'; 6 | import { MaterialModule } from '@app/material.module'; 7 | import { Cloudinary as CloudinaryCore } from 'cloudinary-core'; 8 | import { CloudinaryConfiguration, CloudinaryModule } from '@cloudinary/angular-5.x'; 9 | import { ShellComponent } from './shell.component'; 10 | import { HeaderComponent } from './header/header.component'; 11 | import { GraphqlModule } from './graphql/graphql.module'; 12 | 13 | export const cloudinary = { 14 | Cloudinary: CloudinaryCore 15 | }; 16 | export const config: CloudinaryConfiguration = { 17 | cloud_name: 'cynthesize', 18 | upload_preset: 'qdninpjl' 19 | }; 20 | import { RequestsComponent } from './header/requests/requests.component'; 21 | import { FormsModule, ReactiveFormsModule } from '@angular/forms'; 22 | import { SharedModule } from '@app/shared'; 23 | import { MatInputModule } from '@angular/material'; 24 | 25 | @NgModule({ 26 | imports: [ 27 | CommonModule, 28 | TranslateModule, 29 | FlexLayoutModule, 30 | MaterialModule, 31 | RouterModule, 32 | GraphqlModule, 33 | CloudinaryModule.forRoot(cloudinary, config), 34 | FormsModule, 35 | ReactiveFormsModule, 36 | SharedModule, 37 | MatInputModule 38 | ], 39 | declarations: [HeaderComponent, ShellComponent, RequestsComponent], 40 | entryComponents: [RequestsComponent] 41 | }) 42 | export class ShellModule {} 43 | -------------------------------------------------------------------------------- /src/app/shell/shell.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed, inject } from '@angular/core/testing'; 2 | 3 | import { AuthenticationGuard, AuthenticationService, MockAuthenticationService } from '@app/core'; 4 | import { ShellComponent } from './shell.component'; 5 | import { Shell } from './shell.service'; 6 | 7 | describe('Shell', () => { 8 | beforeEach(() => { 9 | TestBed.configureTestingModule({ 10 | declarations: [ShellComponent], 11 | providers: [AuthenticationGuard, { provide: AuthenticationService, useClass: MockAuthenticationService }] 12 | }); 13 | }); 14 | 15 | describe('childRoutes', () => { 16 | it('should create routes as children of shell', () => { 17 | // Prepare 18 | const testRoutes = [{ path: 'test' }]; 19 | 20 | // Act 21 | const result = Shell.childRoutes(testRoutes); 22 | 23 | // Assert 24 | expect(result.path).toBe(''); 25 | expect(result.children).toBe(testRoutes); 26 | expect(result.component).toBe(ShellComponent); 27 | }); 28 | }); 29 | }); 30 | -------------------------------------------------------------------------------- /src/app/shell/shell.service.ts: -------------------------------------------------------------------------------- 1 | import { Routes, Route } from '@angular/router'; 2 | 3 | import { ShellComponent } from './shell.component'; 4 | 5 | /** 6 | * Provides helper methods to create routes. 7 | */ 8 | export class Shell { 9 | /** 10 | * Creates routes using the shell component and authentication. 11 | * @param routes The routes to add. 12 | * @return {Route} The new route using shell as the base. 13 | */ 14 | static childRoutes(routes: Routes): Route { 15 | return { 16 | path: '', 17 | component: ShellComponent, 18 | children: routes, 19 | // Reuse ShellComponent instance when navigating between child views 20 | data: { reuse: true } 21 | }; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/app/view/feed/feed-project/feed-project.component.html: -------------------------------------------------------------------------------- 1 | 38 | -------------------------------------------------------------------------------- /src/app/view/feed/feed-project/feed-project.component.scss: -------------------------------------------------------------------------------- 1 | .modal-container { 2 | background-color: rgb(247, 247, 247); 3 | } 4 | 5 | modal-dialog-container { 6 | background-color: rgb(247, 247, 247) !important; 7 | } 8 | 9 | .project-heading { 10 | display: flex; 11 | padding: 20px; 12 | align-items: center; 13 | justify-content: space-between; 14 | } 15 | 16 | .project-basic-info { 17 | width: 100%; 18 | margin-left: 30px; 19 | } 20 | 21 | .img-box { 22 | display: flex; 23 | align-items: center; 24 | } 25 | 26 | .project-name { 27 | font-size: 3rem; 28 | font-weight: 700; 29 | } 30 | 31 | .project-abstract { 32 | font-size: 1rem; 33 | color: #5c5c5c; 34 | } 35 | 36 | .project-buttons { 37 | display: flex; 38 | flex-direction: column; 39 | width: 150px; 40 | } 41 | 42 | .project-btn { 43 | color: white; 44 | width: 100%; 45 | padding: 8px; 46 | } 47 | 48 | .orange-btn { 49 | background-color: orange; 50 | } 51 | 52 | @media (max-width: 870px) { 53 | .project-heading { 54 | flex-direction: column; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/app/view/feed/feed-project/feed-project.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { FeedProjectComponent } from './feed-project.component'; 4 | 5 | describe('FeedProjectComponent', () => { 6 | let component: FeedProjectComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [FeedProjectComponent] 12 | }).compileComponents(); 13 | })); 14 | 15 | beforeEach(() => { 16 | fixture = TestBed.createComponent(FeedProjectComponent); 17 | component = fixture.componentInstance; 18 | fixture.detectChanges(); 19 | }); 20 | 21 | it('should create', () => { 22 | expect(component).toBeTruthy(); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /src/app/view/feed/feed-project/feed-project.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit, Inject } from '@angular/core'; 2 | import { MatDialogRef, MAT_DIALOG_DATA, MatBottomSheet } from '@angular/material'; 3 | import { ProjectService } from '@app/core/project/project.service'; 4 | import { ErrorHandlerService } from '@app/core/error-handler.service'; 5 | import { ShareSheetComponent } from '@app/shared/share-sheet/share-sheet.component'; 6 | 7 | @Component({ 8 | selector: 'app-feed-project', 9 | templateUrl: './feed-project.component.html', 10 | styleUrls: ['./feed-project.component.scss'] 11 | }) 12 | export class FeedProjectComponent implements OnInit { 13 | project: any = {}; 14 | constructor( 15 | public dialogRef: MatDialogRef, 16 | @Inject(MAT_DIALOG_DATA) public data: number, 17 | public projectService: ProjectService, 18 | private errorHandler: ErrorHandlerService, 19 | private bottomSheet: MatBottomSheet 20 | ) {} 21 | 22 | onNoClick(): void { 23 | this.dialogRef.close(); 24 | } 25 | 26 | openShareSheet(): void { 27 | this.bottomSheet.open(ShareSheetComponent, { 28 | data: { 29 | facebookUrl: 'https://www.facebook.com/sharer/sharer.php?u=', 30 | twitterUrl: 'https://twitter.com/home?status=', 31 | linkedInUrl: 'https://www.linkedin.com/shareArticle?mini=true&url=&title=&summary=&source=' 32 | } 33 | }); 34 | } 35 | ngOnInit() { 36 | this.projectService.fetchBasicProjectDetails(this.data['activityId']).subscribe( 37 | data => { 38 | this.project = data.data.projects[0]; 39 | }, 40 | error => { 41 | this.errorHandler.subj_notification.next(error); 42 | } 43 | ); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/app/view/feed/feed-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { Routes, RouterModule } from '@angular/router'; 3 | 4 | const routes: Routes = [ 5 | { 6 | path: 'projects', 7 | loadChildren: 'app/view/feed/launched/launched.module#LaunchedModule' 8 | } 9 | ]; 10 | 11 | @NgModule({ 12 | imports: [RouterModule.forChild(routes)], 13 | exports: [RouterModule] 14 | }) 15 | export class FeedRoutingModule {} 16 | -------------------------------------------------------------------------------- /src/app/view/feed/feed.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | 4 | import { FeedRoutingModule } from './feed-routing.module'; 5 | import { IssuesFeedComponent } from './issues-feed/issues-feed.component'; 6 | import { SharedModule } from '@app/shared'; 7 | import { MaterialModule } from '@app/material.module'; 8 | import { NgxPaginationModule } from 'ngx-pagination'; 9 | import { Cloudinary as CloudinaryCore } from 'cloudinary-core'; 10 | import { CloudinaryConfiguration, CloudinaryModule } from '@cloudinary/angular-5.x'; 11 | import { MatBottomSheetModule } from '@angular/material'; 12 | import { FeedProjectComponent } from './feed-project/feed-project.component'; 13 | 14 | export const cloudinary = { 15 | Cloudinary: CloudinaryCore 16 | }; 17 | export const config: CloudinaryConfiguration = { 18 | cloud_name: 'cynthesize', 19 | upload_preset: 'qdninpjl' 20 | }; 21 | 22 | @NgModule({ 23 | declarations: [IssuesFeedComponent, FeedProjectComponent], 24 | imports: [ 25 | CommonModule, 26 | FeedRoutingModule, 27 | SharedModule, 28 | MaterialModule, 29 | MatBottomSheetModule, 30 | NgxPaginationModule, 31 | CloudinaryModule.forRoot(cloudinary, config) 32 | ], 33 | entryComponents: [FeedProjectComponent] 34 | }) 35 | export class FeedModule {} 36 | -------------------------------------------------------------------------------- /src/app/view/feed/issues-feed/issues-feed.component.html: -------------------------------------------------------------------------------- 1 |

issues-feed works!

2 | -------------------------------------------------------------------------------- /src/app/view/feed/issues-feed/issues-feed.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cynthesize/cynthesize-frontend/fc71507b4e7bd8a391956a73d4ced3aac87f0b37/src/app/view/feed/issues-feed/issues-feed.component.scss -------------------------------------------------------------------------------- /src/app/view/feed/issues-feed/issues-feed.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { IssuesFeedComponent } from './issues-feed.component'; 4 | 5 | describe('IssuesFeedComponent', () => { 6 | let component: IssuesFeedComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [IssuesFeedComponent] 12 | }).compileComponents(); 13 | })); 14 | 15 | beforeEach(() => { 16 | fixture = TestBed.createComponent(IssuesFeedComponent); 17 | component = fixture.componentInstance; 18 | fixture.detectChanges(); 19 | }); 20 | 21 | it('should create', () => { 22 | expect(component).toBeTruthy(); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /src/app/view/feed/issues-feed/issues-feed.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-issues-feed', 5 | templateUrl: './issues-feed.component.html', 6 | styleUrls: ['./issues-feed.component.scss'] 7 | }) 8 | export class IssuesFeedComponent implements OnInit { 9 | constructor() {} 10 | 11 | ngOnInit() {} 12 | } 13 | -------------------------------------------------------------------------------- /src/app/view/feed/launched/launched-products/launched-products.component.scss: -------------------------------------------------------------------------------- 1 | .project-icon { 2 | width: 100px; 3 | height: auto; 4 | flex: 1; 5 | border-radius: 6px; 6 | margin-right: 20px; 7 | } 8 | 9 | .apply-role { 10 | display: flex; 11 | align-items: center; 12 | justify-content: space-between; 13 | border-bottom: 1px #dddddd solid; 14 | } 15 | 16 | .launched-project-page-card { 17 | padding: 20px; 18 | margin: 10px 0; 19 | } 20 | 21 | .tags { 22 | padding: 3px; 23 | border: 1px #dddddd solid; 24 | border-radius: 2px; 25 | font-size: 0.7rem; 26 | margin: 0 5px; 27 | font-weight: 400; 28 | } 29 | 30 | .img-box { 31 | display: flex; 32 | align-items: center; 33 | } 34 | -------------------------------------------------------------------------------- /src/app/view/feed/launched/launched-products/launched-products.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { LaunchedProductsComponent } from './launched-products.component'; 4 | 5 | describe('LaunchedProductsComponent', () => { 6 | let component: LaunchedProductsComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [LaunchedProductsComponent] 12 | }).compileComponents(); 13 | })); 14 | 15 | beforeEach(() => { 16 | fixture = TestBed.createComponent(LaunchedProductsComponent); 17 | component = fixture.componentInstance; 18 | fixture.detectChanges(); 19 | }); 20 | 21 | it('should create', () => { 22 | expect(component).toBeTruthy(); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /src/app/view/feed/launched/launched-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { Routes, RouterModule } from '@angular/router'; 3 | import { 4 | LaunchedProductsComponent, 5 | ProjectDialogEntryComponent 6 | } from './launched-products/launched-products.component'; 7 | 8 | const routes: Routes = [ 9 | { 10 | path: '', 11 | component: LaunchedProductsComponent, 12 | children: [ 13 | { 14 | path: ':projectId', 15 | component: ProjectDialogEntryComponent 16 | } 17 | ] 18 | }, 19 | { 20 | path: '**', 21 | redirectTo: '', 22 | pathMatch: 'full' 23 | } 24 | ]; 25 | 26 | @NgModule({ 27 | imports: [RouterModule.forChild(routes)], 28 | exports: [RouterModule] 29 | }) 30 | export class LaunchedRoutingModule {} 31 | -------------------------------------------------------------------------------- /src/app/view/feed/launched/launched.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | 4 | import { LaunchedRoutingModule } from './launched-routing.module'; 5 | import { 6 | LaunchedProductsComponent, 7 | ProjectDialogEntryComponent 8 | } from './launched-products/launched-products.component'; 9 | import { SharedModule } from '@app/shared'; 10 | import { MaterialModule } from '@app/material.module'; 11 | import { FormsModule, ReactiveFormsModule } from '@angular/forms'; 12 | 13 | @NgModule({ 14 | declarations: [LaunchedProductsComponent, ProjectDialogEntryComponent], 15 | imports: [CommonModule, LaunchedRoutingModule, SharedModule, MaterialModule, FormsModule, ReactiveFormsModule], 16 | exports: [LaunchedProductsComponent] 17 | }) 18 | export class LaunchedModule {} 19 | -------------------------------------------------------------------------------- /src/app/view/issues/issue-card/issue-card.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cynthesize/cynthesize-frontend/fc71507b4e7bd8a391956a73d4ced3aac87f0b37/src/app/view/issues/issue-card/issue-card.component.scss -------------------------------------------------------------------------------- /src/app/view/issues/issue-card/issue-card.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { IssueCardComponent } from './issue-card.component'; 4 | 5 | describe('IssueCardComponent', () => { 6 | let component: IssueCardComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [IssueCardComponent] 12 | }).compileComponents(); 13 | })); 14 | 15 | beforeEach(() => { 16 | fixture = TestBed.createComponent(IssueCardComponent); 17 | component = fixture.componentInstance; 18 | fixture.detectChanges(); 19 | }); 20 | 21 | it('should create', () => { 22 | expect(component).toBeTruthy(); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /src/app/view/issues/issue-card/issue-card.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit, Input } from '@angular/core'; 2 | import { ProjectService } from '@app/core/project/project.service'; 3 | 4 | @Component({ 5 | selector: 'app-issue-card', 6 | templateUrl: './issue-card.component.html', 7 | styleUrls: ['./issue-card.component.scss'] 8 | }) 9 | export class IssueCardComponent implements OnInit { 10 | @Input() issue: any; 11 | 12 | constructor(public projectService: ProjectService) {} 13 | 14 | ngOnInit() {} 15 | } 16 | -------------------------------------------------------------------------------- /src/app/view/issues/issue-feed.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 6 |
7 | 8 | 9 | 10 |
11 |
12 | 13 |
14 | 15 | -------------------------------------------------------------------------------- /src/app/view/issues/issue-feed.component.scss: -------------------------------------------------------------------------------- 1 | .issue-card { 2 | margin: 20px 0; 3 | } 4 | -------------------------------------------------------------------------------- /src/app/view/issues/issue-feed.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { IssueFeedComponent } from './issue-feed.component'; 4 | 5 | describe('IssueFeedComponent', () => { 6 | let component: IssueFeedComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [IssueFeedComponent] 12 | }).compileComponents(); 13 | })); 14 | 15 | beforeEach(() => { 16 | fixture = TestBed.createComponent(IssueFeedComponent); 17 | component = fixture.componentInstance; 18 | fixture.detectChanges(); 19 | }); 20 | 21 | it('should create', () => { 22 | expect(component).toBeTruthy(); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /src/app/view/issues/issue-feed.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { IssueService } from '@app/core/issue/issue.service'; 3 | import { ErrorHandlerService } from '@app/core/error-handler.service'; 4 | import { Router } from '@angular/router'; 5 | 6 | @Component({ 7 | selector: 'app-issue-feed', 8 | templateUrl: './issue-feed.component.html', 9 | styleUrls: ['./issue-feed.component.scss'] 10 | }) 11 | export class IssueFeedComponent implements OnInit { 12 | isLoading = false; 13 | openIssuesList: any[] = []; 14 | 15 | constructor(private issueService: IssueService, private errorHandler: ErrorHandlerService, private router: Router) { 16 | if (localStorage.getItem('is_mentor') !== 'true') { 17 | this.router.navigate(['/']); 18 | } 19 | this.issueService.fetchOpenIssues().subscribe( 20 | (data: any) => { 21 | this.openIssuesList = data.data.issues; 22 | }, 23 | (error: any) => { 24 | this.errorHandler.subj_notification.next(error); 25 | } 26 | ); 27 | } 28 | 29 | ngOnInit() {} 30 | } 31 | -------------------------------------------------------------------------------- /src/app/view/issues/issues-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { Routes, RouterModule } from '@angular/router'; 3 | import { IssueFeedComponent } from './issue-feed.component'; 4 | 5 | const routes: Routes = [ 6 | { 7 | path: '', 8 | component: IssueFeedComponent 9 | } 10 | ]; 11 | 12 | @NgModule({ 13 | imports: [RouterModule.forChild(routes)], 14 | exports: [RouterModule] 15 | }) 16 | export class IssuesRoutingModule {} 17 | -------------------------------------------------------------------------------- /src/app/view/issues/issues.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | 4 | import { IssuesRoutingModule } from './issues-routing.module'; 5 | import { IssueFeedComponent } from './issue-feed.component'; 6 | import { IssueCardComponent } from './issue-card/issue-card.component'; 7 | import { MaterialModule } from '@app/material.module'; 8 | import { SharedModule } from '@app/shared'; 9 | import { FormsModule, ReactiveFormsModule } from '@angular/forms'; 10 | 11 | @NgModule({ 12 | declarations: [IssueFeedComponent, IssueCardComponent], 13 | imports: [CommonModule, IssuesRoutingModule, SharedModule, FormsModule, ReactiveFormsModule, MaterialModule] 14 | }) 15 | export class IssuesModule {} 16 | -------------------------------------------------------------------------------- /src/app/view/review-answers/checkpoint-tab/action-modal/action-modal.component.html: -------------------------------------------------------------------------------- 1 |
2 |

You are submitting review for {{ data.project_name }}

3 | 4 | 5 | 6 |
7 | 8 | 11 | 12 |
13 |
14 | 15 | 20 | -------------------------------------------------------------------------------- /src/app/view/review-answers/checkpoint-tab/action-modal/action-modal.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cynthesize/cynthesize-frontend/fc71507b4e7bd8a391956a73d4ced3aac87f0b37/src/app/view/review-answers/checkpoint-tab/action-modal/action-modal.component.scss -------------------------------------------------------------------------------- /src/app/view/review-answers/checkpoint-tab/action-modal/action-modal.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { ActionModalComponent } from './action-modal.component'; 4 | 5 | describe('ActionModalComponent', () => { 6 | let component: ActionModalComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ActionModalComponent] 12 | }).compileComponents(); 13 | })); 14 | 15 | beforeEach(() => { 16 | fixture = TestBed.createComponent(ActionModalComponent); 17 | component = fixture.componentInstance; 18 | fixture.detectChanges(); 19 | }); 20 | 21 | it('should create', () => { 22 | expect(component).toBeTruthy(); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /src/app/view/review-answers/checkpoint-tab/action-modal/action-modal.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit, Inject, Input } from '@angular/core'; 2 | import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material'; 3 | import { ReviewService } from '@app/core/review/review.service'; 4 | import { ErrorHandlerService } from '@app/core/error-handler.service'; 5 | 6 | @Component({ 7 | selector: 'app-action-modal', 8 | templateUrl: './action-modal.component.html', 9 | styleUrls: ['./action-modal.component.scss'] 10 | }) 11 | export class ActionModalComponent implements OnInit { 12 | reviewComment = 'Looks good to me!'; 13 | constructor( 14 | public dialogRef: MatDialogRef, 15 | @Inject(MAT_DIALOG_DATA) public data: any, 16 | private reviewService: ReviewService, 17 | private errorHandler: ErrorHandlerService 18 | ) {} 19 | 20 | onNoClick(): void { 21 | this.dialogRef.close(); 22 | } 23 | ngOnInit() {} 24 | actionOnCheckpoint() { 25 | this.reviewService 26 | .actionOnCheckpointByMentor( 27 | this.reviewComment, 28 | this.data.stageId, 29 | this.data.stageType, 30 | this.data.isApproved, 31 | this.data.projectId 32 | ) 33 | .subscribe( 34 | (data: any) => { 35 | this.dialogRef.close(); 36 | this.errorHandler.subj_notification.next('Review Submitted!'); 37 | }, 38 | error => { 39 | this.errorHandler.subj_notification.next(error); 40 | } 41 | ); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/app/view/review-answers/checkpoint-tab/checkpoint-tab.component.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 |
5 |
6 | {{ reviewAnswer.project.project_name }} 7 |
8 | {{ projectService.displayableName(reviewAnswer.project.project_name) }} 9 |
10 | 11 |
12 |
13 | {{ question.value }} : {{ reviewAnswer[question.key] }} 14 |
15 |
16 |
17 | 18 | 21 | 24 | 25 |
26 |
27 |
28 | 29 |
30 |
31 | -------------------------------------------------------------------------------- /src/app/view/review-answers/checkpoint-tab/checkpoint-tab.component.scss: -------------------------------------------------------------------------------- 1 | .header-image { 2 | width: 13px; 3 | height: 13px; 4 | margin: 10px 25px; 5 | } 6 | 7 | .card-header { 8 | display: flex; 9 | align-items: center; 10 | margin: 10px 0; 11 | } 12 | 13 | mat-card-actions { 14 | display: flex; 15 | justify-content: flex-end; 16 | } 17 | 18 | .review-answers { 19 | margin: 10px 0; 20 | } 21 | 22 | .flex-center img { 23 | width: 300px; 24 | } 25 | -------------------------------------------------------------------------------- /src/app/view/review-answers/checkpoint-tab/checkpoint-tab.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { CheckpointTabComponent } from './checkpoint-tab.component'; 4 | 5 | describe('CheckpointTabComponent', () => { 6 | let component: CheckpointTabComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [CheckpointTabComponent] 12 | }).compileComponents(); 13 | })); 14 | 15 | beforeEach(() => { 16 | fixture = TestBed.createComponent(CheckpointTabComponent); 17 | component = fixture.componentInstance; 18 | fixture.detectChanges(); 19 | }); 20 | 21 | it('should create', () => { 22 | expect(component).toBeTruthy(); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /src/app/view/review-answers/review-answers-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { Routes, RouterModule } from '@angular/router'; 3 | import { ReviewAnswersComponent } from './review-answers.component'; 4 | import { CheckpointTabComponent } from './checkpoint-tab/checkpoint-tab.component'; 5 | 6 | const routes: Routes = [ 7 | { 8 | path: '', 9 | component: ReviewAnswersComponent, 10 | children: [ 11 | { 12 | path: ':checkpointName', 13 | component: CheckpointTabComponent 14 | } 15 | ], 16 | data: { title: 'Cynthesize | Stage Review' } 17 | } 18 | ]; 19 | 20 | @NgModule({ 21 | imports: [RouterModule.forChild(routes)], 22 | exports: [RouterModule] 23 | }) 24 | export class ReviewAnswersRoutingModule {} 25 | -------------------------------------------------------------------------------- /src/app/view/review-answers/review-answers.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 6 | 18 | 19 | 20 |
21 |
22 | -------------------------------------------------------------------------------- /src/app/view/review-answers/review-answers.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cynthesize/cynthesize-frontend/fc71507b4e7bd8a391956a73d4ced3aac87f0b37/src/app/view/review-answers/review-answers.component.scss -------------------------------------------------------------------------------- /src/app/view/review-answers/review-answers.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { ReviewAnswersComponent } from './review-answers.component'; 4 | 5 | describe('ReviewAnswersComponent', () => { 6 | let component: ReviewAnswersComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ReviewAnswersComponent] 12 | }).compileComponents(); 13 | })); 14 | 15 | beforeEach(() => { 16 | fixture = TestBed.createComponent(ReviewAnswersComponent); 17 | component = fixture.componentInstance; 18 | fixture.detectChanges(); 19 | }); 20 | 21 | it('should create', () => { 22 | expect(component).toBeTruthy(); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /src/app/view/review-answers/review-answers.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { ReviewService } from '@app/core/review/review.service'; 3 | 4 | @Component({ 5 | selector: 'app-review-answers', 6 | templateUrl: './review-answers.component.html', 7 | styleUrls: ['./review-answers.component.scss'] 8 | }) 9 | export class ReviewAnswersComponent implements OnInit { 10 | checkpoints = ['Ideation', 'Prototyping', 'Feedback', 'Launching', 'Funding']; 11 | constructor(private reviewService: ReviewService) {} 12 | 13 | ngOnInit() {} 14 | } 15 | -------------------------------------------------------------------------------- /src/app/view/review-answers/review-answers.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | 4 | import { ReviewAnswersRoutingModule } from './review-answers-routing.module'; 5 | import { ReviewAnswersComponent } from './review-answers.component'; 6 | import { MaterialModule } from '@app/material.module'; 7 | import { SharedModule } from '@app/shared'; 8 | import { CheckpointTabComponent } from './checkpoint-tab/checkpoint-tab.component'; 9 | import { ActionModalComponent } from './checkpoint-tab/action-modal/action-modal.component'; 10 | import { FormsModule, ReactiveFormsModule } from '@angular/forms'; 11 | 12 | @NgModule({ 13 | declarations: [ReviewAnswersComponent, CheckpointTabComponent, ActionModalComponent], 14 | imports: [CommonModule, ReviewAnswersRoutingModule, MaterialModule, SharedModule, ReactiveFormsModule, FormsModule], 15 | entryComponents: [ActionModalComponent] 16 | }) 17 | export class ReviewAnswersModule {} 18 | -------------------------------------------------------------------------------- /src/app/view/view-project/issue/add-issue.html: -------------------------------------------------------------------------------- 1 |
2 |

Add Issue

3 |
4 |
{{ errorMessage }}
5 |
6 | 7 | 8 | -- 9 | {{ checkpoint }} 10 | 11 | Please choose a checkpoint. 12 | 13 | 14 | 15 |

16 | Please enter detailed description for the issue you're facing. You wouldn't want to waste your time on 17 | explaining it in the comments. 18 |

19 |
20 | 21 |
22 |
23 |
24 | 25 | 26 |
27 |
28 | -------------------------------------------------------------------------------- /src/app/view/view-project/issue/issue.component.scss: -------------------------------------------------------------------------------- 1 | .checkpoint-name { 2 | font-size: 3rem; 3 | } 4 | 5 | .issue-expansion { 6 | border: 1px #dddddd solid; 7 | } 8 | 9 | .issue-list { 10 | display: flex; 11 | flex-direction: column; 12 | flex: 3; 13 | width: 100%; 14 | } 15 | 16 | .issue-tab { 17 | display: flex; 18 | } 19 | 20 | .nav-container { 21 | margin-left: 30px; 22 | font-size: 1rem; 23 | width: 100%; 24 | } 25 | 26 | .nav-container mat-card { 27 | margin-bottom: 15px; 28 | } 29 | 30 | .checkpoint-nav-name { 31 | border-left: 2px blue solid; 32 | padding-left: 15px; 33 | color: black; 34 | } 35 | .category-issue { 36 | margin: 15px 0; 37 | } 38 | 39 | .issue-icon { 40 | width: 30px; 41 | font-size: 1.5rem; 42 | } 43 | .green { 44 | color: #0acb7b; 45 | } 46 | 47 | .no-issue { 48 | width: 300px; 49 | height: 300px; 50 | } 51 | -------------------------------------------------------------------------------- /src/app/view/view-project/issue/issue.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { IssueComponent } from './issue.component'; 4 | 5 | describe('IssueComponent', () => { 6 | let component: IssueComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [IssueComponent] 12 | }).compileComponents(); 13 | })); 14 | 15 | beforeEach(() => { 16 | fixture = TestBed.createComponent(IssueComponent); 17 | component = fixture.componentInstance; 18 | fixture.detectChanges(); 19 | }); 20 | 21 | it('should create', () => { 22 | expect(component).toBeTruthy(); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /src/app/view/view-project/project-home/project-home.component.scss: -------------------------------------------------------------------------------- 1 | .home-content { 2 | margin: 10px; 3 | justify-content: space-between; 4 | } 5 | 6 | .description-fields { 7 | display: flex; 8 | align-items: center; 9 | font-size: initial; 10 | margin: 5px 0; 11 | } 12 | 13 | .description-fields b { 14 | margin: 5px; 15 | flex: 1; 16 | } 17 | 18 | .description-text { 19 | margin: 5px; 20 | flex: 2; 21 | } 22 | 23 | .description-fields mat-form-field { 24 | flex: 2; 25 | } 26 | 27 | .description-container { 28 | display: flex; 29 | justify-content: space-between; 30 | } 31 | 32 | .left-column { 33 | flex: 3; 34 | margin-right: 20px; 35 | } 36 | 37 | .right-column { 38 | flex: 1; 39 | } 40 | 41 | .project-home-heads { 42 | display: flex; 43 | align-items: center; 44 | justify-content: space-between; 45 | } 46 | -------------------------------------------------------------------------------- /src/app/view/view-project/project-home/project-home.component.spec.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable:no-unused-variable */ 2 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 3 | import { By } from '@angular/platform-browser'; 4 | import { DebugElement } from '@angular/core'; 5 | 6 | import { ProjectHomeComponent } from './project-home.component'; 7 | 8 | describe('ProjectHomeComponent', () => { 9 | let component: ProjectHomeComponent; 10 | let fixture: ComponentFixture; 11 | 12 | beforeEach(async(() => { 13 | TestBed.configureTestingModule({ 14 | declarations: [ProjectHomeComponent] 15 | }).compileComponents(); 16 | })); 17 | 18 | beforeEach(() => { 19 | fixture = TestBed.createComponent(ProjectHomeComponent); 20 | component = fixture.componentInstance; 21 | fixture.detectChanges(); 22 | }); 23 | 24 | it('should create', () => { 25 | expect(component).toBeTruthy(); 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /src/app/view/view-project/project-home/review-responses/review-responses.component.html: -------------------------------------------------------------------------------- 1 |
2 |

Reviews for your {{ data.projectName }}'s submission:

3 |
4 |
9 |
10 |
11 | 12 |
13 |
14 | {{ 15 | review.mentor_datum.user.username 16 | }} 17 |

{{ review.review_comments }}

18 |
19 |
20 |
21 |
22 | Sorry, no mentor has reviewed your submission. Hold tight! 23 |
24 |
25 |
26 | -------------------------------------------------------------------------------- /src/app/view/view-project/project-home/review-responses/review-responses.component.scss: -------------------------------------------------------------------------------- 1 | .review-comment { 2 | padding: 10px; 3 | border: 1px #dddddd solid; 4 | border-radius: 5px; 5 | margin-bottom: 10px; 6 | } 7 | 8 | .declined { 9 | border-right: 4px red solid; 10 | } 11 | 12 | .approved { 13 | border-right: 4px greenyellow solid; 14 | } 15 | -------------------------------------------------------------------------------- /src/app/view/view-project/project-home/review-responses/review-responses.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { ReviewResponsesComponent } from './review-responses.component'; 4 | 5 | describe('ReviewResponsesComponent', () => { 6 | let component: ReviewResponsesComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ReviewResponsesComponent] 12 | }).compileComponents(); 13 | })); 14 | 15 | beforeEach(() => { 16 | fixture = TestBed.createComponent(ReviewResponsesComponent); 17 | component = fixture.componentInstance; 18 | fixture.detectChanges(); 19 | }); 20 | 21 | it('should create', () => { 22 | expect(component).toBeTruthy(); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /src/app/view/view-project/project-home/review-responses/review-responses.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit, Inject } from '@angular/core'; 2 | import { ReviewService } from '@app/core/review/review.service'; 3 | import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material'; 4 | 5 | @Component({ 6 | selector: 'app-review-responses', 7 | templateUrl: './review-responses.component.html', 8 | styleUrls: ['./review-responses.component.scss'] 9 | }) 10 | export class ReviewResponsesComponent implements OnInit { 11 | reviewAnswer: any; 12 | 13 | constructor( 14 | private reviewService: ReviewService, 15 | public dialogRef: MatDialogRef, 16 | @Inject(MAT_DIALOG_DATA) public data: any 17 | ) { 18 | this.reviewService.fetchReviewAnswersByMentors(this.data.projectId).subscribe((res: any) => { 19 | this.reviewAnswer = res.data.review_comments; 20 | }); 21 | } 22 | 23 | ngOnInit() {} 24 | } 25 | -------------------------------------------------------------------------------- /src/app/view/view-project/project-progress/project-progress.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cynthesize/cynthesize-frontend/fc71507b4e7bd8a391956a73d4ced3aac87f0b37/src/app/view/view-project/project-progress/project-progress.component.scss -------------------------------------------------------------------------------- /src/app/view/view-project/project-progress/project-progress.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { ProjectProgressComponent } from './project-progress.component'; 4 | 5 | describe('ProjectProgressComponent', () => { 6 | let component: ProjectProgressComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ProjectProgressComponent] 12 | }).compileComponents(); 13 | })); 14 | 15 | beforeEach(() => { 16 | fixture = TestBed.createComponent(ProjectProgressComponent); 17 | component = fixture.componentInstance; 18 | fixture.detectChanges(); 19 | }); 20 | 21 | it('should create', () => { 22 | expect(component).toBeTruthy(); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /src/app/view/view-project/project-progress/project-progress.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit, Input } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-project-progress', 5 | templateUrl: './project-progress.component.html', 6 | styleUrls: ['./project-progress.component.scss'] 7 | }) 8 | export class ProjectProgressComponent implements OnInit { 9 | @Input() currentStage = ''; 10 | completedStages = { 11 | ideation_stage: false, 12 | prototype_development_stage: false, 13 | consumer_feedback_stage: false, 14 | launching_stage: false, 15 | funding_stage: false 16 | }; 17 | constructor() {} 18 | 19 | ngOnInit() { 20 | for (let index = 0; index < Object.keys(this.completedStages).length; index++) { 21 | const stage = Object.keys(this.completedStages)[index]; 22 | if (stage === this.currentStage) { 23 | break; 24 | } else { 25 | this.completedStages[stage] = true; 26 | } 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/app/view/view-project/project-timeline/project-timeline.component.html: -------------------------------------------------------------------------------- 1 | 2 |
3 |

Timeline

4 | 7 |
8 |
Add dates for the project's acheivements and the date by when you'll submit your project for review.
9 |
10 |
{{ errorMessage }}
11 |
12 |
13 | 14 | Add an event 15 | 16 | 17 | 18 | 19 | 20 | 22 | 23 | 26 | 29 |
30 |
31 |
32 |

{{ event.value }}

33 | {{ event.key | date }} 34 |
35 |
36 | -------------------------------------------------------------------------------- /src/app/view/view-project/project-timeline/project-timeline.component.scss: -------------------------------------------------------------------------------- 1 | .add-event { 2 | display: flex; 3 | align-items: baseline; 4 | justify-content: space-between; 5 | } 6 | 7 | .timeline-event { 8 | padding: 10px; 9 | opacity: 0.8; 10 | border-left: 1px rgb(112, 112, 112) solid; 11 | } 12 | 13 | .event-name { 14 | font-weight: 700; 15 | font-size: 1rem; 16 | } 17 | -------------------------------------------------------------------------------- /src/app/view/view-project/project-timeline/project-timeline.component.spec.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable:no-unused-variable */ 2 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 3 | import { By } from '@angular/platform-browser'; 4 | import { DebugElement } from '@angular/core'; 5 | 6 | import { ProjectTimelineComponent } from './project-timeline.component'; 7 | 8 | describe('ProjectTimelineComponent', () => { 9 | let component: ProjectTimelineComponent; 10 | let fixture: ComponentFixture; 11 | 12 | beforeEach(async(() => { 13 | TestBed.configureTestingModule({ 14 | declarations: [ProjectTimelineComponent] 15 | }).compileComponents(); 16 | })); 17 | 18 | beforeEach(() => { 19 | fixture = TestBed.createComponent(ProjectTimelineComponent); 20 | component = fixture.componentInstance; 21 | fixture.detectChanges(); 22 | }); 23 | 24 | it('should create', () => { 25 | expect(component).toBeTruthy(); 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /src/app/view/view-project/project-timeline/project-timeline.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit, Input } from '@angular/core'; 2 | import { FormGroup, FormBuilder } from '@angular/forms'; 3 | import { ProjectService } from '@app/core/project/project.service'; 4 | import { ErrorHandlerService } from '@app/core/error-handler.service'; 5 | 6 | @Component({ 7 | selector: 'app-project-timeline', 8 | templateUrl: './project-timeline.component.html', 9 | styleUrls: ['./project-timeline.component.scss'] 10 | }) 11 | export class ProjectTimelineComponent implements OnInit { 12 | @Input() timeline: any; 13 | @Input() projectId: number; 14 | 15 | addingTimelineEvent = false; 16 | timelineDataForm: FormGroup; 17 | 18 | errorMessage = ''; 19 | 20 | constructor( 21 | private formBuilder: FormBuilder, 22 | private projectService: ProjectService, 23 | private errorHandler: ErrorHandlerService 24 | ) {} 25 | 26 | ngOnInit() {} 27 | 28 | initAddTimelineEvent() { 29 | this.timelineDataForm = this.formBuilder.group({ 30 | eventName: [''], 31 | date: [Date.now()] 32 | }); 33 | this.addingTimelineEvent = true; 34 | } 35 | 36 | addTimelineEvent() { 37 | if (this.timelineDataForm.get('eventName').value.trim() === '') { 38 | this.errorMessage = 'Please enter name of the event.'; 39 | } else { 40 | this.errorMessage = ''; 41 | this.timeline[Date.parse(this.timelineDataForm.get('date').value)] = this.timelineDataForm.get('eventName').value; 42 | this.projectService.updateProjectEvents({ timeline: this.timeline }, this.projectId).subscribe( 43 | (data: any) => { 44 | this.timelineDataForm.get('eventName').setValue(''); 45 | this.addingTimelineEvent = false; 46 | }, 47 | (error: any) => { 48 | this.errorHandler.subj_notification.next(error); 49 | } 50 | ); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/app/view/view-project/review/review.component.html: -------------------------------------------------------------------------------- 1 |
2 | Review Submission 3 | 4 | | 5 | {{ 6 | data.context 7 | .split('_') 8 | .join(' ') 9 | .charAt(0) 10 | .toUpperCase() + 11 | data.context 12 | .split('_') 13 | .join(' ') 14 | .slice(1) 15 | }} 17 |
18 |
19 | Apply for the next stage? All the best from Cynthesize! Make sure that you answer the questions fully and upto 20 | the mark. 21 |
22 |
23 |
24 | 25 |
26 |
27 |
28 | {{ errorMessage }} 29 |
30 |
31 | 32 | 33 | Question {{ i + 1 }} of {{ totalQuestions }} 34 |
{{ control.value }}
35 | 36 | 37 | 38 |
39 | 40 | 41 | 50 |
51 |
52 |
53 |
54 |
55 |
56 | 57 |
58 | 59 |
Good luck for your submission... Mentors are reviewing your answers.
60 |
61 | -------------------------------------------------------------------------------- /src/app/view/view-project/review/review.component.scss: -------------------------------------------------------------------------------- 1 | .heading { 2 | font-size: 3rem; 3 | font-weight: 700; 4 | background: #e7e7e7; 5 | padding: 20px 20px 0 20px; 6 | } 7 | .review-form-field { 8 | width: 100%; 9 | } 10 | .waiting { 11 | display: flex; 12 | flex-direction: column; 13 | align-items: center; 14 | justify-content: center; 15 | font-size: 1.5rem; 16 | } 17 | .review-modal { 18 | display: flex; 19 | font-family: "Open sans", sans-serif; 20 | } 21 | .info-text { 22 | font-family: "Open sans", sans-serif; 23 | font-size: 1rem; 24 | background: #e7e7e7; 25 | padding: 0 20px; 26 | } 27 | .flex-center > img { 28 | height: 270px; 29 | width: 300px; 30 | } 31 | .image-container { 32 | background-color: #e7e7e7; 33 | width: 100%; 34 | } 35 | .waiting img { 36 | width: 55%; 37 | margin: 70px 0; 38 | } 39 | .waiting div { 40 | margin-bottom: 20px; 41 | } 42 | -------------------------------------------------------------------------------- /src/app/view/view-project/review/review.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { ReviewComponent } from './review.component'; 4 | 5 | describe('ReviewComponent', () => { 6 | let component: ReviewComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ReviewComponent] 12 | }).compileComponents(); 13 | })); 14 | 15 | beforeEach(() => { 16 | fixture = TestBed.createComponent(ReviewComponent); 17 | component = fixture.componentInstance; 18 | fixture.detectChanges(); 19 | }); 20 | 21 | it('should create', () => { 22 | expect(component).toBeTruthy(); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /src/app/view/view-project/view-project-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { Routes, RouterModule } from '@angular/router'; 3 | import { ViewProjectComponent } from './view-project.component'; 4 | import { IssueComponent } from './issue/issue.component'; 5 | 6 | const routes: Routes = [ 7 | { 8 | path: '', 9 | component: ViewProjectComponent, 10 | children: [ 11 | { 12 | path: 'issues', 13 | component: IssueComponent 14 | } 15 | ] 16 | } 17 | ]; 18 | 19 | @NgModule({ 20 | imports: [RouterModule.forChild(routes)], 21 | exports: [RouterModule] 22 | }) 23 | export class ViewRoutingModule {} 24 | -------------------------------------------------------------------------------- /src/app/view/view-project/view-project.component.scss: -------------------------------------------------------------------------------- 1 | .project-page { 2 | display: flex; 3 | flex-direction: column; 4 | } 5 | .project-basic-info { 6 | display: flex; 7 | align-items: center; 8 | color: white; 9 | padding: 40px 0; 10 | flex-basis: 90%; 11 | } 12 | .project-header-info { 13 | display: flex; 14 | max-width: 70%; 15 | } 16 | .project-header { 17 | background-color: #2196f3; 18 | display: flex; 19 | align-items: center; 20 | justify-content: center; 21 | } 22 | .project-header-name { 23 | font-size: 3.6rem; 24 | font-weight: 700; 25 | display: flex; 26 | align-items: center; 27 | } 28 | .project-platform { 29 | border-radius: 3px; 30 | background-color: white; 31 | color: #686868; 32 | font-weight: 500; 33 | font-size: 0.8rem; 34 | padding: 2px 5px; 35 | margin-left: 5px; 36 | } 37 | .project-abstract { 38 | padding-top: 5px; 39 | font-size: 1.1rem; 40 | max-width: 85%; 41 | } 42 | .project-user-info { 43 | font-size: 0.8rem; 44 | } 45 | .project-user-info a { 46 | color: white; 47 | } 48 | .project-icon { 49 | margin-right: 45px; 50 | } 51 | .project-buttons { 52 | background-color: white; 53 | } 54 | .buttons-container { 55 | display: flex; 56 | align-items: center; 57 | justify-content: center; 58 | } 59 | .project-nav-button { 60 | margin: 0 10px; 61 | } 62 | -------------------------------------------------------------------------------- /src/app/view/view-project/view-project.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { ViewProjectComponent } from './view-project.component'; 4 | 5 | describe('ViewProjectComponent', () => { 6 | let component: ViewProjectComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ViewProjectComponent] 12 | }).compileComponents(); 13 | })); 14 | 15 | beforeEach(() => { 16 | fixture = TestBed.createComponent(ViewProjectComponent); 17 | component = fixture.componentInstance; 18 | fixture.detectChanges(); 19 | }); 20 | 21 | it('should create', () => { 22 | expect(component).toBeTruthy(); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /src/app/view/view-project/view-project.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | 4 | import { ViewRoutingModule } from './view-project-routing.module'; 5 | import { ViewProjectComponent } from './view-project.component'; 6 | import { SharedModule } from '@app/shared'; 7 | import { MaterialModule } from '@app/material.module'; 8 | import { ReactiveFormsModule, FormsModule } from '@angular/forms'; 9 | import { IssueComponent, AddIssueComponent } from './issue/issue.component'; 10 | import { CovalentMarkdownModule } from '@covalent/markdown'; 11 | import { CovalentTextEditorModule } from '@covalent/text-editor'; 12 | import { ReviewComponent } from './review/review.component'; 13 | import { ProjectHomeComponent } from './project-home/project-home.component'; 14 | import { ProjectTimelineComponent } from './project-timeline/project-timeline.component'; 15 | import { ChartsModule } from 'ng2-charts'; 16 | import { ProjectProgressComponent } from './project-progress/project-progress.component'; 17 | import { ReviewResponsesComponent } from './project-home/review-responses/review-responses.component'; 18 | 19 | @NgModule({ 20 | declarations: [ 21 | ViewProjectComponent, 22 | IssueComponent, 23 | AddIssueComponent, 24 | ReviewComponent, 25 | ProjectHomeComponent, 26 | ProjectTimelineComponent, 27 | ProjectProgressComponent, 28 | ReviewResponsesComponent 29 | ], 30 | imports: [ 31 | CommonModule, 32 | ViewRoutingModule, 33 | SharedModule, 34 | MaterialModule, 35 | ReactiveFormsModule, 36 | CovalentMarkdownModule, 37 | ChartsModule, 38 | CovalentTextEditorModule, 39 | FormsModule 40 | ], 41 | entryComponents: [ 42 | AddIssueComponent, 43 | ReviewComponent, 44 | ProjectHomeComponent, 45 | ProjectTimelineComponent, 46 | ReviewResponsesComponent 47 | ] 48 | }) 49 | export class ViewProjectModule {} 50 | -------------------------------------------------------------------------------- /src/app/view/view-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { Routes, RouterModule } from '@angular/router'; 3 | 4 | const routes: Routes = [ 5 | { 6 | path: 'issues', 7 | loadChildren: 'app/view/issues/issues.module#IssuesModule' 8 | }, 9 | { 10 | path: 'project/:id', 11 | loadChildren: 'app/view/view-project/view-project.module#ViewProjectModule' 12 | }, 13 | { 14 | path: 'review-answers', 15 | loadChildren: 'app/view/review-answers/review-answers.module#ReviewAnswersModule' 16 | }, 17 | { 18 | path: 'feed', 19 | loadChildren: 'app/view/feed/feed.module#FeedModule' 20 | } 21 | ]; 22 | 23 | @NgModule({ 24 | imports: [RouterModule.forChild(routes)], 25 | exports: [RouterModule] 26 | }) 27 | export class ViewRoutingModule {} 28 | -------------------------------------------------------------------------------- /src/app/view/view.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | 4 | import { ViewRoutingModule } from './view-routing.module'; 5 | import { SharedModule } from '@app/shared'; 6 | import { ChartsModule } from 'ng2-charts'; 7 | 8 | @NgModule({ 9 | declarations: [], 10 | imports: [CommonModule, ViewRoutingModule, SharedModule, ChartsModule], 11 | entryComponents: [] 12 | }) 13 | export class ViewModule {} 14 | -------------------------------------------------------------------------------- /src/assets/cynthesize-banner-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cynthesize/cynthesize-frontend/fc71507b4e7bd8a391956a73d4ced3aac87f0b37/src/assets/cynthesize-banner-logo.png -------------------------------------------------------------------------------- /src/assets/cynthesize-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cynthesize/cynthesize-frontend/fc71507b4e7bd8a391956a73d4ced3aac87f0b37/src/assets/cynthesize-logo.png -------------------------------------------------------------------------------- /src/assets/fonts/Gilroy-Black.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cynthesize/cynthesize-frontend/fc71507b4e7bd8a391956a73d4ced3aac87f0b37/src/assets/fonts/Gilroy-Black.ttf -------------------------------------------------------------------------------- /src/assets/fonts/Gilroy-BlackItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cynthesize/cynthesize-frontend/fc71507b4e7bd8a391956a73d4ced3aac87f0b37/src/assets/fonts/Gilroy-BlackItalic.ttf -------------------------------------------------------------------------------- /src/assets/fonts/Gilroy-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cynthesize/cynthesize-frontend/fc71507b4e7bd8a391956a73d4ced3aac87f0b37/src/assets/fonts/Gilroy-Bold.ttf -------------------------------------------------------------------------------- /src/assets/fonts/Gilroy-BoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cynthesize/cynthesize-frontend/fc71507b4e7bd8a391956a73d4ced3aac87f0b37/src/assets/fonts/Gilroy-BoldItalic.ttf -------------------------------------------------------------------------------- /src/assets/fonts/Gilroy-ExtraBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cynthesize/cynthesize-frontend/fc71507b4e7bd8a391956a73d4ced3aac87f0b37/src/assets/fonts/Gilroy-ExtraBold.ttf -------------------------------------------------------------------------------- /src/assets/fonts/Gilroy-ExtraBoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cynthesize/cynthesize-frontend/fc71507b4e7bd8a391956a73d4ced3aac87f0b37/src/assets/fonts/Gilroy-ExtraBoldItalic.ttf -------------------------------------------------------------------------------- /src/assets/fonts/Gilroy-Heavy.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cynthesize/cynthesize-frontend/fc71507b4e7bd8a391956a73d4ced3aac87f0b37/src/assets/fonts/Gilroy-Heavy.ttf -------------------------------------------------------------------------------- /src/assets/fonts/Gilroy-HeavyItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cynthesize/cynthesize-frontend/fc71507b4e7bd8a391956a73d4ced3aac87f0b37/src/assets/fonts/Gilroy-HeavyItalic.ttf -------------------------------------------------------------------------------- /src/assets/fonts/Gilroy-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cynthesize/cynthesize-frontend/fc71507b4e7bd8a391956a73d4ced3aac87f0b37/src/assets/fonts/Gilroy-Light.ttf -------------------------------------------------------------------------------- /src/assets/fonts/Gilroy-LightItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cynthesize/cynthesize-frontend/fc71507b4e7bd8a391956a73d4ced3aac87f0b37/src/assets/fonts/Gilroy-LightItalic.ttf -------------------------------------------------------------------------------- /src/assets/fonts/Gilroy-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cynthesize/cynthesize-frontend/fc71507b4e7bd8a391956a73d4ced3aac87f0b37/src/assets/fonts/Gilroy-Medium.ttf -------------------------------------------------------------------------------- /src/assets/fonts/Gilroy-MediumItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cynthesize/cynthesize-frontend/fc71507b4e7bd8a391956a73d4ced3aac87f0b37/src/assets/fonts/Gilroy-MediumItalic.ttf -------------------------------------------------------------------------------- /src/assets/fonts/Gilroy-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cynthesize/cynthesize-frontend/fc71507b4e7bd8a391956a73d4ced3aac87f0b37/src/assets/fonts/Gilroy-Regular.ttf -------------------------------------------------------------------------------- /src/assets/fonts/Gilroy-RegularItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cynthesize/cynthesize-frontend/fc71507b4e7bd8a391956a73d4ced3aac87f0b37/src/assets/fonts/Gilroy-RegularItalic.ttf -------------------------------------------------------------------------------- /src/assets/fonts/Gilroy-SemiBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cynthesize/cynthesize-frontend/fc71507b4e7bd8a391956a73d4ced3aac87f0b37/src/assets/fonts/Gilroy-SemiBold.ttf -------------------------------------------------------------------------------- /src/assets/fonts/Gilroy-SemiBoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cynthesize/cynthesize-frontend/fc71507b4e7bd8a391956a73d4ced3aac87f0b37/src/assets/fonts/Gilroy-SemiBoldItalic.ttf -------------------------------------------------------------------------------- /src/assets/fonts/Gilroy-Thin.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cynthesize/cynthesize-frontend/fc71507b4e7bd8a391956a73d4ced3aac87f0b37/src/assets/fonts/Gilroy-Thin.ttf -------------------------------------------------------------------------------- /src/assets/fonts/Gilroy-ThinItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cynthesize/cynthesize-frontend/fc71507b4e7bd8a391956a73d4ced3aac87f0b37/src/assets/fonts/Gilroy-ThinItalic.ttf -------------------------------------------------------------------------------- /src/assets/fonts/Gilroy-UltraLight.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cynthesize/cynthesize-frontend/fc71507b4e7bd8a391956a73d4ced3aac87f0b37/src/assets/fonts/Gilroy-UltraLight.ttf -------------------------------------------------------------------------------- /src/assets/fonts/Gilroy-UltraLightItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cynthesize/cynthesize-frontend/fc71507b4e7bd8a391956a73d4ced3aac87f0b37/src/assets/fonts/Gilroy-UltraLightItalic.ttf -------------------------------------------------------------------------------- /src/assets/images/full.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cynthesize/cynthesize-frontend/fc71507b4e7bd8a391956a73d4ced3aac87f0b37/src/assets/images/full.png -------------------------------------------------------------------------------- /src/assets/images/home_bg_1.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/images/ideas.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cynthesize/cynthesize-frontend/fc71507b4e7bd8a391956a73d4ced3aac87f0b37/src/assets/images/ideas.png -------------------------------------------------------------------------------- /src/assets/images/profile-selected.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/images/profile.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/images/project.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cynthesize/cynthesize-frontend/fc71507b4e7bd8a391956a73d4ced3aac87f0b37/src/assets/images/project.png -------------------------------------------------------------------------------- /src/assets/images/pw_maze_black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cynthesize/cynthesize-frontend/fc71507b4e7bd8a391956a73d4ced3aac87f0b37/src/assets/images/pw_maze_black.png -------------------------------------------------------------------------------- /src/assets/intersection.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/logos/Asset 2MY.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cynthesize/cynthesize-frontend/fc71507b4e7bd8a391956a73d4ced3aac87f0b37/src/assets/logos/Asset 2MY.png -------------------------------------------------------------------------------- /src/assets/logos/Asset 3MY.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cynthesize/cynthesize-frontend/fc71507b4e7bd8a391956a73d4ced3aac87f0b37/src/assets/logos/Asset 3MY.png -------------------------------------------------------------------------------- /src/assets/logos/Asset 4MY.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cynthesize/cynthesize-frontend/fc71507b4e7bd8a391956a73d4ced3aac87f0b37/src/assets/logos/Asset 4MY.png -------------------------------------------------------------------------------- /src/assets/logos/profilePicture.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cynthesize/cynthesize-frontend/fc71507b4e7bd8a391956a73d4ced3aac87f0b37/src/assets/logos/profilePicture.jpg -------------------------------------------------------------------------------- /src/assets/logos/profilePicture2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cynthesize/cynthesize-frontend/fc71507b4e7bd8a391956a73d4ced3aac87f0b37/src/assets/logos/profilePicture2.jpg -------------------------------------------------------------------------------- /src/assets/user.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cynthesize/cynthesize-frontend/fc71507b4e7bd8a391956a73d4ced3aac87f0b37/src/assets/user.png -------------------------------------------------------------------------------- /src/browserslist: -------------------------------------------------------------------------------- 1 | # This file is currently used by autoprefixer to adjust CSS to support the below specified browsers 2 | # For additional information regarding the format and rule options, please see: 3 | # https://github.com/browserslist/browserslist#queries 4 | 5 | last 2 versions 6 | > 0.5% 7 | Firefox ESR 8 | not dead 9 | # For IE 9-11 support, please uncomment the next line and adjust as needed 10 | # IE 9-11 11 | -------------------------------------------------------------------------------- /src/environments/environment.prod.ts: -------------------------------------------------------------------------------- 1 | // `.env.ts` is generated by the `npm run env` command 2 | 3 | export const environment = { 4 | production: true, 5 | version: '1.0.0', 6 | serverUrl: 'https://api.chucknorris.io', 7 | defaultLanguage: 'en-US', 8 | supportedLanguages: ['en-US', 'fr-FR'] 9 | }; 10 | export const GRAPHQL_URL = 'https://cynthesize-hasura.herokuapp.com/v1alpha1/graphql'; 11 | export const REALTIME_GRAPHQL_URL = 'wss://cynthesize-hasura.herokuapp.com/v1alpha1/graphql'; 12 | export const authClientId = 'tEbTH2wRl3dHtWZ0m4nqoWPsN1GIu9dQ'; 13 | export const authDomain = 'cynthesize.auth0.com'; 14 | export const auth0Audience = 'https://cynthesize.auth0.com/userinfo'; 15 | export const callbackUrl = 'https://cynthesize-develop.netlify.app/'; 16 | -------------------------------------------------------------------------------- /src/environments/environment.ts: -------------------------------------------------------------------------------- 1 | // The file contents for the current environment will overwrite these during build. 2 | // The build system defaults to the dev environment which uses `environment.ts`, but if you do 3 | // `ng build --prod` then `environment.prod.ts` will be used instead. 4 | // The list of which env maps to which file can be found in `.angular-cli.json`. 5 | 6 | // `.env.ts` is generated by the `npm run env` command 7 | 8 | export const environment = { 9 | production: false, 10 | version: '1.0.0-dev', 11 | serverUrl: '/api', 12 | defaultLanguage: 'en-US', 13 | supportedLanguages: ['en-US'] 14 | }; 15 | export const GRAPHQL_URL = 'https://cynthesize-develop.herokuapp.com/v1alpha1/graphql'; 16 | export const REALTIME_GRAPHQL_URL = 'wss://cynthesize-develop.herokuapp.com/v1alpha1/graphql'; 17 | export const authClientId = 'tEbTH2wRl3dHtWZ0m4nqoWPsN1GIu9dQ'; 18 | export const authDomain = 'cynthesize.auth0.com'; 19 | export const auth0Audience = 'https://cynthesize.auth0.com/userinfo'; 20 | export const callbackUrl = 'http://localhost:4200'; 21 | -------------------------------------------------------------------------------- /src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cynthesize/cynthesize-frontend/fc71507b4e7bd8a391956a73d4ced3aac87f0b37/src/favicon.ico -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Cynthesize 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /src/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration file, see link for more information 2 | // https://karma-runner.github.io/1.0/config/configuration-file.html 3 | 4 | process.env.CHROME_BIN = require('puppeteer').executablePath(); 5 | const path = require('path'); 6 | 7 | module.exports = function(config) { 8 | config.set({ 9 | basePath: '..', 10 | frameworks: ['jasmine', '@angular-devkit/build-angular'], 11 | plugins: [ 12 | require('karma-jasmine'), 13 | require('karma-chrome-launcher'), 14 | require('karma-junit-reporter'), 15 | require('karma-coverage-istanbul-reporter'), 16 | require('@angular-devkit/build-angular/plugins/karma') 17 | ], 18 | client: { 19 | clearContext: false, // leave Jasmine Spec Runner output visible in browser 20 | captureConsole: Boolean(process.env.KARMA_ENABLE_CONSOLE) 21 | }, 22 | junitReporter: { 23 | outputDir: path.join(__dirname, '../reports/junit/'), 24 | outputFile: 'TESTS-xunit.xml', 25 | useBrowserName: false, 26 | suite: '' // Will become the package name attribute in xml testsuite element 27 | }, 28 | coverageIstanbulReporter: { 29 | reports: ['html', 'lcovonly', 'text-summary'], 30 | dir: path.join(__dirname, '../reports/coverage'), 31 | fixWebpackSourcePaths: true 32 | }, 33 | angularCli: { 34 | environment: 'dev' 35 | }, 36 | reporters: ['progress', 'junit'], 37 | port: 9876, 38 | colors: true, 39 | // Level of logging, can be: LOG_DISABLE || LOG_ERROR || LOG_WARN || LOG_INFO || LOG_DEBUG 40 | logLevel: config.LOG_INFO, 41 | autoWatch: true, 42 | browsers: ['ChromeHeadless'], 43 | singleRun: false 44 | }); 45 | }; 46 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Entry point of the application. 3 | * Only platform bootstrapping code should be here. 4 | * For app-specific initialization, use `app/app.component.ts`. 5 | */ 6 | 7 | import 'hammerjs'; 8 | import { enableProdMode } from '@angular/core'; 9 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 10 | 11 | import { AppModule } from '@app/app.module'; 12 | import { environment } from '@env/environment'; 13 | 14 | if (environment.production) { 15 | enableProdMode(); 16 | } 17 | 18 | platformBrowserDynamic() 19 | .bootstrapModule(AppModule) 20 | .catch(err => console.log(err)); 21 | -------------------------------------------------------------------------------- /src/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cynthesize", 3 | "short_name": "cynthesize", 4 | "theme_color": "#488aff", 5 | "background_color": "#488aff", 6 | "scope": "/", 7 | "start_url": "/", 8 | "display": "standalone", 9 | "icons": [{ 10 | "src": "assets/cynthesize-logo.png", 11 | "sizes": "512x512", 12 | "type": "image/png" 13 | }] 14 | } 15 | -------------------------------------------------------------------------------- /src/robots.txt: -------------------------------------------------------------------------------- 1 | # www.robotstxt.org/ 2 | 3 | # Allow crawling of all content 4 | User-agent: * 5 | Disallow: 6 | -------------------------------------------------------------------------------- /src/sass/_declarations.scss: -------------------------------------------------------------------------------- 1 | @import "./variables"; 2 | @import "./mixins"; 3 | @import "./typography"; 4 | @import "./rules"; 5 | -------------------------------------------------------------------------------- /src/sass/_mixins.scss: -------------------------------------------------------------------------------- 1 | //Positioning 2 | 3 | @mixin absoluteCenter { 4 | position: absolute; 5 | top: 50%; 6 | left: 50%; 7 | 8 | transform: translate(-50%, -50%); 9 | } 10 | 11 | //Effects 12 | -------------------------------------------------------------------------------- /src/sass/_rules.scss: -------------------------------------------------------------------------------- 1 | .v-desktop { 2 | display: block; 3 | 4 | @media (max-width: 37.5em) { 5 | display: none; 6 | } 7 | } 8 | 9 | .v-mobile { 10 | display: none; 11 | 12 | @media (max-width: 37.5em) { 13 | display: block; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/sass/_variables.scss: -------------------------------------------------------------------------------- 1 | //Colors 2 | 3 | $color-white: #ffffff; 4 | $color-black: #000000; 5 | 6 | $color-primary: #00bcd4; 7 | $color-primary-light: #b2ebf2; 8 | $color-primary-dark: #0080ff; 9 | 10 | $color-accent: #00bcd4; 11 | 12 | $color-primary-text: #212121; 13 | $color-secondary-text: #757575; 14 | $color-divider: #bdbdbd; 15 | 16 | //Shadow Values 17 | 18 | $shadow-light: 0rem 1rem 4rem rgba(0, 0, 0, 0.1); 19 | 20 | //Border Radius Values 21 | 22 | $radius-small: 3px; 23 | $radius-medium: 0.5rem; 24 | $radius-large: 1rem; 25 | 26 | $gw-m: 3rem; //gutterwidth 27 | 28 | //Breakpoints 29 | 30 | $bp-12: 75em; //1200px 1em = 16px always for breakpoints 31 | $bp-11: 68.75em; //1100px 32 | $bp-9: 56.25em; //900px 33 | $bp-8: 50em; //900px 34 | $bp-6: 37.5em; //600px 35 | $bp-5: 31.25em; //500px 36 | -------------------------------------------------------------------------------- /src/sass/components/_login-register.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cynthesize/cynthesize-frontend/fc71507b4e7bd8a391956a73d4ced3aac87f0b37/src/sass/components/_login-register.scss -------------------------------------------------------------------------------- /src/test.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Prepare environment for unit tests. 3 | * This file is required by karma.conf.js and loads recursively all the .spec and framework files. 4 | */ 5 | 6 | import 'zone.js/dist/zone-testing'; 7 | import { getTestBed } from '@angular/core/testing'; 8 | import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; 9 | 10 | declare const require: any; 11 | 12 | // First, initialize the Angular testing environment. 13 | getTestBed().initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting()); 14 | // Then we find all the tests. 15 | const context = require.context('./', true, /\.spec\.ts$/); 16 | // And load the modules. 17 | context.keys().map(context); 18 | -------------------------------------------------------------------------------- /src/theme/theme-variables.scss: -------------------------------------------------------------------------------- 1 | /* 2 | * Application global variables. 3 | */ 4 | 5 | // Angular Material custom theme 6 | // See https://material.angular.io/guide/theming for more details. 7 | // 8 | // You can also read https://medium.com/@tomastrajan/the-complete-guide-to-angular-material-themes-4d165a9d24d1 9 | // for more insight about Angular Material theming. 10 | 11 | @import "~@angular/material/theming"; 12 | 13 | // Define the palettes for your theme using the Material Design palettes available in palette.scss 14 | // (imported above). For each palette, you can optionally specify a default, lighter, and darker 15 | // hue. 16 | $app-primary: mat-palette($mat-blue); 17 | $app-accent: mat-palette($mat-pink, A200, A100, A400); 18 | 19 | // The warn palette is optional (defaults to red). 20 | $app-warn: mat-palette($mat-red); 21 | 22 | // Create the theme object (a Sass map containing all of the palettes). 23 | $app-theme: mat-light-theme($app-primary, $app-accent, $app-warn); 24 | -------------------------------------------------------------------------------- /src/theme/theme.scss: -------------------------------------------------------------------------------- 1 | /* 2 | * Global application theme. 3 | * Framework overrides and customization goes here. 4 | */ 5 | 6 | // stylelint-disable-next-line selector-max-universal 7 | * { 8 | box-sizing: border-box; 9 | } 10 | 11 | html, 12 | body { 13 | display: flex; 14 | flex-direction: column; 15 | margin: 0; 16 | height: 100%; 17 | } 18 | -------------------------------------------------------------------------------- /src/translations/en-US.json: -------------------------------------------------------------------------------- 1 | { 2 | "APP_NAME": "Cynthesize", 3 | "About": "About", 4 | "Hello world !": "Hello world !", 5 | "Home": "Home", 6 | "Logged in as": "Logged in as", 7 | "Login": "Login", 8 | "Logout": "Logout", 9 | "Password": "Password", 10 | "Password is required": "Password is required", 11 | "Username": "Username", 12 | "Username is required": "Username is required", 13 | "Username or password incorrect.": "Username or password incorrect.", 14 | "Remember me": "Remember me", 15 | "Version": "Version" 16 | } 17 | -------------------------------------------------------------------------------- /src/translations/fr-FR.json: -------------------------------------------------------------------------------- 1 | { 2 | "APP_NAME": "Cynthesize", 3 | "About": "A propos", 4 | "Hello world !": "Bonjour le monde !", 5 | "Home": "Accueil", 6 | "Logged in as": "Connecté en tant que", 7 | "Login": "Connexion", 8 | "Logout": "Déconnexion", 9 | "Password": "Mot de passe", 10 | "Password is required": "Mot de passe requis", 11 | "Username": "Identifiant", 12 | "Username is required": "Identifiant requis", 13 | "Username or password incorrect.": "Identifiant ou mot de passe incorrect.", 14 | "Remember me": "Rester connecté", 15 | "Version": "Version" 16 | } 17 | -------------------------------------------------------------------------------- /src/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/app", 5 | "baseUrl": "./", 6 | "module": "es2015", 7 | "types": [] 8 | }, 9 | "exclude": [ 10 | "test.ts", 11 | "**/*.spec.ts", 12 | "**/*.mock.ts" 13 | ], 14 | "angularCompilerOptions": { 15 | "preserveWhitespaces": true 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/spec", 5 | "baseUrl": "./", 6 | "module": "commonjs", 7 | "target": "es5", 8 | "types": [ 9 | "jasmine", 10 | "node" 11 | ] 12 | }, 13 | "files": [ 14 | "test.ts", 15 | "polyfills.ts" 16 | ], 17 | "include": [ 18 | "**/*.spec.ts", 19 | "**/*.mock.ts", 20 | "**/*.d.ts" 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /src/typings.d.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Extra typings definitions 3 | */ 4 | 5 | // Allow .json files imports 6 | declare module '*.json'; 7 | 8 | // SystemJS module definition 9 | declare var module: NodeModule; 10 | interface NodeModule { 11 | id: string; 12 | } 13 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "outDir": "./dist/out-tsc", 5 | "sourceMap": true, 6 | "declaration": false, 7 | "moduleResolution": "node", 8 | "emitDecoratorMetadata": true, 9 | "experimentalDecorators": true, 10 | "noImplicitAny": true, 11 | "suppressImplicitAnyIndexErrors": true, 12 | "target": "es5", 13 | "typeRoots": ["node_modules/@types"], 14 | "lib": ["es2017", "dom", "esnext.asynciterable"], 15 | "baseUrl": "src", 16 | "paths": { 17 | "@app/*": ["app/*"], 18 | "@env/*": ["environments/*"] 19 | } 20 | } 21 | } 22 | --------------------------------------------------------------------------------