diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 8e7048ff4..baf7184fe 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -4,8 +4,11 @@ packages/ @dev-launchers/platform-enablement # Ideaspace code apps/ideaspace/ @dev-launchers/ideaspace-team +# UserProfile code +apps/user-profile/ @dev-launchers/User-profile + # Recruitment code -apps/dev-recruiters/ @dev-launchers/dev-recruit-team +apps/dev-recruiters/ @dev-launchers/dev-recruit # Website code apps/website/ @Enjoy2Live @pyxld-kris @@ -14,7 +17,7 @@ apps/website/ @Enjoy2Live @pyxld-kris apps/app/ @Enjoy2Live @pyxld-kris # Code in the backend directory -strapiv4/ @dev-launchers/backend-devs +strapiv4/ @dev-launchers/Backend-Devs # Deps -package.json @Enjoy2Live \ No newline at end of file +package.json @Enjoy2Live diff --git a/.github/workflows/chromatic.yml b/.github/workflows/chromatic.yml index f2db41728..7c5c9836a 100644 --- a/.github/workflows/chromatic.yml +++ b/.github/workflows/chromatic.yml @@ -15,17 +15,22 @@ jobs: deploy: # The operating system it will run on runs-on: ubuntu-latest - continue-on-error: true outputs: url: ${{ steps.chromatic.outputs.url }} buildUrl: ${{ steps.chromatic.outputs.buildUrl }} storybookUrl: ${{ steps.chromatic.outputs.storybookUrl }} code: ${{ steps.chromatic.outputs.code }} + branch: ${{ steps.extract_branch.outputs.branch }} # The list of steps that the action will go through steps: - name: Checkout uses: actions/checkout@v1 - + + - name: Extract branch name + shell: bash + run: echo "branch=$(echo ${GITHUB_REF#refs/heads/})" >>$GITHUB_OUTPUT + id: extract_branch + - name: Use Node.js 16.x uses: actions/setup-node@v3 with: @@ -35,6 +40,9 @@ jobs: - name: install deps run: corepack enable && yarn + - name: generate css + run: yarn workspace @devlaunchers/tailwind build-tw + #👇 Adds Chromatic as a step in the workflow - name: Chromatic uses: chromaui/action@v1 @@ -46,16 +54,7 @@ jobs: projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }} token: ${{ secrets.GITHUB_TOKEN }} onlyChanged: true - - - name: Discord Channel Notification - env: - DISCORD_WEBHOOK: ${{ secrets.UI_BUILDS_WEBHOOK }} - DISCORD_EMBEDS: '[{"title": "UI changes are ready for review: ${{ github.event.pull_request.comment }}","description": "Started by ${{ github.event.pull_request.user.login }}. Click [here](${{ steps.chromatic.outputs.buildUrl }}) to view the Chromatic review.","url": "${{steps.chromatic.outputs.storybookUrl}}"}]' - - uses: Ilshidur/action-discord@master - with: - args: 'New build for {{ EVENT_PAYLOAD.repository.full_name }} Finished.' - - name: Run Storybook tests - run: yarn test-storybook - env: - TARGET_URL: ${{ steps.chromatic.outputs.storybookUrl }} +# - name: Run Storybook tests +# run: yarn test-storybook +# env: +# TARGET_URL: ${{ steps.chromatic.outputs.storybookUrl }} diff --git a/.github/workflows/create-tokens.yml b/.github/workflows/create-tokens.yml new file mode 100644 index 000000000..009c43cec --- /dev/null +++ b/.github/workflows/create-tokens.yml @@ -0,0 +1,24 @@ +name: CI +on: + push: + branches: + - "development/design-team" + paths: + - "packages/tailwind-constructor/tokens.json" + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Setup Node.js environment + uses: actions/setup-node@v2.4.0 + # Install dependencies + - run: yarn + # Transform Figma Tokens JSON to something Style Dictionary can read, run Style Dictionary and later run Tailwind + - run: yarn workspace @devlaunchers/tailwind build + # Add files that were created during a run, e.g. created files from style dictionary or token-transformer. + - uses: stefanzweifel/git-auto-commit-action@v4 + with: + commit_message: Update Tokens diff --git a/.github/workflows/discord-webhook.yml b/.github/workflows/discord-webhook.yml new file mode 100644 index 000000000..6d56f5a67 --- /dev/null +++ b/.github/workflows/discord-webhook.yml @@ -0,0 +1,35 @@ +name: Discord Webhook +on: + pull_request: + paths: + - 'packages/UI/.storybook/**' + - 'packages/UI/src/**' +jobs: + discord: + runs-on: ubuntu-latest + outputs: + url: ${{ steps.chromatic.outputs.url }} + buildUrl: ${{ steps.chromatic.outputs.buildUrl }} + storybookUrl: ${{ steps.chromatic.outputs.storybookUrl }} + branch: ${{ steps.extract_branch.outputs.branch }} + pr_number: ${{ steps.extract_pr_number.outputs.pr_number }} + steps: + - name: Checkout + uses: actions/checkout@v2 + - name: Extract branch name + shell: bash + run: echo "branch=$(echo "${{ github.event.pull_request.head.ref }}" | tr / -)" >> $GITHUB_OUTPUT + id: extract_branch + - name: Extract PR number + shell: bash + run: echo "pr_number=$(echo "$GITHUB_REF" | awk -F / '{print $3}')" >> $GITHUB_OUTPUT + id: extract_pr_number + - name: Discord Channel Notification + env: + BRANCH: ${{ steps.extract_branch.outputs.branch }} + URL: ${{ steps.deploy.outputs.url }} + DISCORD_WEBHOOK: ${{ secrets.UI_BUILDS_WEBHOOK }} + DISCORD_EMBEDS: '[{"title": "Chromatic","description": "View the Chromatic review.","url": "https://www.chromatic.com/pullrequest?appId=632ca21aeea1618b7fa0bf48&number=${{ steps.extract_pr_number.outputs.pr_number }}"}, {"title": "Storybook","description": "View the storybook for this change.","url": "https://${{ steps.extract_branch.outputs.branch }}--632ca21aeea1618b7fa0bf48.chromatic.com/"}]' + uses: Ilshidur/action-discord@master + with: + args: 'New build for ${{ steps.extract_branch.outputs.branch }} has finished. ${{ github.event.head_commit.author.name }} has made changes with the following commit message: ${{ github.event.pull_request.head_commit.message }}' diff --git a/.github/workflows/staging-apps.yaml b/.github/workflows/staging-apps.yaml index 1bf238c29..a24992568 100644 --- a/.github/workflows/staging-apps.yaml +++ b/.github/workflows/staging-apps.yaml @@ -11,6 +11,10 @@ on: - 'staging/projects[1-2]' - 'staging/website' - 'staging/website[1-2]' + - 'staging/user-profile' + - 'staging/user-profile[1-2]' + - 'staging/gptbot' + - 'staging/gptbot[1-2]' jobs: Publish: @@ -42,6 +46,15 @@ jobs: shell: bash run: "sed -ie 's$unoptimized: false$unoptimized: true$g' ./apps/app/next.config.js" + - name: Change environment to the app specific subdomains + run: "sed -ie 's$https://staging.devlaunchers.org$https://${{ steps.extract_branch.outputs.branch }}.devlaunchers.org$g' ./apps/app/.env.staging" + + - name: Change environment to staging + run: mv apps/app/.env.staging apps/app/.env.production + + - name: generate css + run: yarn workspace @devlaunchers/tailwind build-tw + - name: Run build run: yarn workspace @devlaunchers/app build @@ -49,6 +62,7 @@ jobs: run: yarn workspace @devlaunchers/app export - name: Publish + if: ${{ !env.ACT }} uses: cloudflare/pages-action@v1 with: apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} @@ -62,16 +76,19 @@ jobs: #I'm doing this because in cloudflare pages dashboard I created these projects -#- 'ideaspace' -#- 'ideaspace1' -#- 'ideaspace2' -#- 'recruitment' -#- 'recruitment1' -#- 'recruitment2' -#- 'projects' -#- 'projects1' -#- 'projects2' -#- 'website' -#- 'website1' -#- 'website2' +#- 'ideaspace' => ideaspace.pages.dev +#- 'ideaspace1' => ideaspace1.pages.dev +#- 'ideaspace2' => ideaspace2.pages.dev +#- 'recruitment' => recruitment-djw.pages.dev +#- 'recruitment1' => recruitment1.pages.dev +#- 'recruitment2' => recruitment2.pages.dev +#- 'projects' => projects-2w7.pages.dev +#- 'projects1' => projects1-1ut.pages.dev +#- 'projects2' => projects2.pages.dev +#- 'website' => website-386.pages.dev +#- 'website1' => website1-c59.pages.dev +#- 'website2' => website2-7s8.pages.dev +#- 'user-profile' => user-profile-4zw.pages.dev +#- 'user-profile1' => user-profile1.pages.dev +#- 'user-profile2' => user-profile2.pages.dev #and on every commit that touches the branches of these teams I deploy to the app from the branch name extraction step: Extract branch name diff --git a/.github/workflows/staging-storybook.yml b/.github/workflows/staging-storybook.yml deleted file mode 100644 index 605d49594..000000000 --- a/.github/workflows/staging-storybook.yml +++ /dev/null @@ -1,57 +0,0 @@ -# Simple workflow for deploying static content to GitHub Pages -name: staging Storybook to Gh-pages - -on: - # Runs on pushes targeting the default branch - push: - branches: ["master"] - - # Allows you to run this workflow manually from the Actions tab - workflow_dispatch: - -defaults: - run: - working-directory: packages/UI - -# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages -permissions: - contents: read - pages: write - id-token: write - -# Allow one concurrent deployment -concurrency: - group: "pages" - cancel-in-progress: true - -jobs: - # Single deploy job since we're just deploying - deploy: - environment: - name: github-pages - url: ${{ steps.deployment.outputs.page_url }} - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v3 - - name: Use Node.js 16.x - uses: actions/setup-node@v3 - with: - node-version: 16.x - cache: yarn - - name: install deps - run: corepack enable && yarn workspaces focus - - name: build storybook - run: yarn build-storybook - - name: Setup Pages - uses: actions/configure-pages@v3 - - name: print list of files - run: ls - - name: Upload artifact - uses: actions/upload-pages-artifact@v1 - with: - # Upload the static website of storybook - path: './storybook-static' - - name: Deploy to GitHub Pages - id: deployment - uses: actions/deploy-pages@v1 diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 94221f6a5..9ccf18143 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -30,4 +30,6 @@ jobs: push: false # Get cache from latest build on main branch cache-from: type=gha - # Don't set cache-to which will export the cache \ No newline at end of file + # Don't set cache-to which will export the cache + build-args: | + NODE_ENV=staging diff --git a/.gitignore b/.gitignore index 5007cdd6a..e248ada02 100644 --- a/.gitignore +++ b/.gitignore @@ -69,3 +69,6 @@ yarn-error.log* # Storybook storybook-static + +# Tailwind +packages/tailwind-constructor/tailwind.css \ No newline at end of file diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100644 index 000000000..896f6b186 --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1,5 @@ +#!/bin/sh +. "$(dirname "$0")/_/husky.sh" + +yarn g:lint-staged-files +# yarn g:codegen diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 000000000..4b74fd57d --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,3 @@ +{ + "recommendations": ["dsznajder.es7-react-js-snippets","dbaeumer.vscode-eslint", "bradlc.vscode-tailwindcss","figma.figma-vscode-extension" ], +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 000000000..0d172f77c --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,6 @@ +{ + "tailwindCSS.experimental.configFile": "packages/tailwind-constructor/tailwind.config.js", + "tailwindCSS.experimental.classRegex": [ + ["tv\\((([^()]*|\\([^()]*\\))*)\\)", "[\"'`]([^\"'`]*).*?[\"'`]"] + ] +} diff --git a/.vscode/typescriptreact.code-snippets b/.vscode/typescriptreact.code-snippets new file mode 100644 index 000000000..f928c527d --- /dev/null +++ b/.vscode/typescriptreact.code-snippets @@ -0,0 +1,51 @@ +{ + "Create a component": { + "prefix": "comp", + "body": [ + "import * as React from 'react';", + "import { tv, type VariantProps } from 'tailwind-variants';", + "", + "const $1Styles = tv({", + " slots: {$2},", + " variants: {$3},", + "});", + "", + "interface $1Props extends VariantProps {}", + "", + "const $1 = ({}: $1Props) => {", + " return
$0
;", + "};", + "", + "$1.displayName = '$1';", + "export default $1;", + "" + ], + "description": "Create a component" + }, + "create a sample story": { + "prefix": "story", + "body": [ + "import type { Meta, StoryObj } from '@storybook/react';", + "", + "import $1 from './$1';", + "", + "const meta: Meta = {", + " component: $1,", + "};", + "", + "export default meta;", + "type Story = StoryObj;", + "", + "/*", + " *👇 Render functions are a framework specific feature to allow you control on how the component renders.", + " * See https://storybook.js.org/docs/react/api/csf", + " * to learn how to use render functions.", + " */", + "export const Primary: Story = {", + " render: () => <$1 />,", + "};", + "" + ], + "description": "create a sample story" + } +} \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 361b5a633..13994132b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -61,6 +61,9 @@ ENV PRISMA_CLI_BINARY_TARGETS=linux-musl # Does not play well with buildkit on CI # https://github.com/moby/buildkit/issues/1673 +# Make sure the env is set to be CI +ENV CI=true + RUN --mount=type=cache,target=/root/.yarn3-cache,id=yarn3-cache \ YARN_CACHE_FOLDER=/root/.yarn3-cache \ yarn install --immutable --inline-builds @@ -74,6 +77,8 @@ FROM node:14.19.3-bullseye AS builder ARG NODE_ENV=production ENV NEXTJS_IGNORE_ESLINT=1 ENV NEXTJS_IGNORE_TYPECHECK=0 +# Make sure the env is set to be CI +ENV CI=true WORKDIR /app @@ -84,6 +89,7 @@ COPY --from=deps /workspace-install ./ RUN if [ "${NODE_ENV}" = "staging" ]; then mv ./apps/app/.env.staging ./apps/app/.env.production ; fi # # Optional: if the app depends on global /static shared assets like images, locales... +RUN yarn workspace @devlaunchers/tailwind build-tw RUN yarn workspace @devlaunchers/app build # Does not play well with buildkit on CI @@ -112,12 +118,18 @@ COPY --from=builder /app/apps/website/next.config.js \ COPY --from=builder /app/apps/ideaspace/next.config.js \ /app/apps/ideaspace/package.json \ ./apps/ideaspace/ +COPY --from=builder /app/apps/user-profile/next.config.js \ + /app/apps/user-profile/package.json \ + ./apps/user-profile/ COPY --from=builder /app/apps/site-projects/next.config.js \ /app/apps/site-projects/package.json \ ./apps/site-projects/ COPY --from=builder /app/apps/dev-recruiters/next.config.js \ /app/apps/dev-recruiters/package.json \ ./apps/dev-recruiters/ +COPY --from=builder /app/apps/gptbot/next.config.js \ + /app/apps/gptbot/package.json \ + ./apps/gptbot/ COPY --from=builder /app/apps/app/public ./apps/app/public COPY --from=builder --chown=nextjs:nodejs /app/apps/app/.next ./apps/app/.next COPY --from=builder --chown=nextjs:nodejs /app/apps/app/.env.production ./apps/app/.env.production diff --git a/README.md b/README.md index 5f3053a3d..dab4e6945 100644 --- a/README.md +++ b/README.md @@ -22,11 +22,11 @@ Visit https://www.volunteermatch.org/s/srp/orgOpps?org=1189675 to join one of ou 1. Clone the repo -2. Install: `yarn` +2. Install dependencies: `yarn install` -3. Install project dependencies: `yarn install` +3. Run the tailwindcss compiler: `yarn workspace @devlaunchers/tailwind dev` -4. Run dev version: `yarn workspace @devlaunchers/app dev` +4. Run the app's development server: `yarn workspace @devlaunchers/app dev` --- diff --git a/apps/app/.env.development b/apps/app/.env.development index 48dd1a498..0247faf95 100644 --- a/apps/app/.env.development +++ b/apps/app/.env.development @@ -1,5 +1,7 @@ NEXT_PUBLIC_NAME=DEVELOPMENT -NEXT_PUBLIC_API_URL=https://api-staging.devlaunchers.org -NEXT_PUBLIC_STRAPI_URL=https://api-staging.devlaunchers.org -NEXT_PUBLIC_GOOGLE_AUTH_URL=https://api-staging.devlaunchers.org/connect/google -NEXT_PUBLIC_DISCORD_AUTH_URL=https://discord.com/api/oauth2/authorize?client_id=815294711983112194&redirect_uri=https%3A%2F%2Fapi-staging.devlaunchers.org%2Fusers%2Fauth%2Fdiscord%2Fcallback&response_type=code&scope=identify \ No newline at end of file +NEXT_PUBLIC_API_BASE_URL=https://apiv4-staging.devlaunchers.org +NEXT_PUBLIC_API_URL=https://apiv4-staging.devlaunchers.org/api +NEXT_PUBLIC_STRAPI_URL=https://apiv4-staging.devlaunchers.org/api +NEXT_PUBLIC_GOOGLE_AUTH_URL=https://apiv4-staging.devlaunchers.org/api/connect/google +NEXT_PUBLIC_DISCORD_AUTH_URL=https://discord.com/api/oauth2/authorize?client_id=815294711983112194&redirect_uri=https%3A%2F%2Fapi-staging.devlaunchers.org%2Fusers%2Fauth%2Fdiscord%2Fcallback&response_type=code&scope=identify +NEXT_PUBLIC_FRONT_END_URL=http://localhost:3000 \ No newline at end of file diff --git a/apps/app/.env.production b/apps/app/.env.production index b61e83afa..2eabbaf15 100644 --- a/apps/app/.env.production +++ b/apps/app/.env.production @@ -1,5 +1,7 @@ NEXT_PUBLIC_NAME=PRODUCTION -NEXT_PUBLIC_API_URL=https://api.devlaunchers.org -NEXT_PUBLIC_STRAPI_URL=https://api.devlaunchers.org -NEXT_PUBLIC_GOOGLE_AUTH_URL=https://api.devlaunchers.org/connect/google -NEXT_PUBLIC_DISCORD_AUTH_URL=https://discord.com/api/oauth2/authorize?client_id=709889509864636496&redirect_uri=https%3A%2F%2Fapi.devlaunchers.org%2Fusers%2Fauth%2Fdiscord%2Fcallback&response_type=code&scope=identify \ No newline at end of file +NEXT_PUBLIC_API_BASE_URL=https://apiv4.devlaunchers.org +NEXT_PUBLIC_API_URL=https://apiv4.devlaunchers.org/api +NEXT_PUBLIC_STRAPI_URL=https://apiv4.devlaunchers.org/api +NEXT_PUBLIC_GOOGLE_AUTH_URL=https://apiv4.devlaunchers.org/api/connect/google +NEXT_PUBLIC_DISCORD_AUTH_URL=https://discord.com/api/oauth2/authorize?client_id=709889509864636496&redirect_uri=https%3A%2F%2Fapi.devlaunchers.org%2Fusers%2Fauth%2Fdiscord%2Fcallback&response_type=code&scope=identify +NEXT_PUBLIC_FRONT_END_URL=https://devlaunchers.org \ No newline at end of file diff --git a/apps/app/.env.staging b/apps/app/.env.staging index 50456d86c..c32ae635f 100644 --- a/apps/app/.env.staging +++ b/apps/app/.env.staging @@ -1,5 +1,7 @@ NEXT_PUBLIC_NAME=STAGING -NEXT_PUBLIC_API_URL=https://api-staging.devlaunchers.org -NEXT_PUBLIC_STRAPI_URL=https://api-staging.devlaunchers.org -NEXT_PUBLIC_GOOGLE_AUTH_URL=https://api-staging.devlaunchers.org/connect/google -NEXT_PUBLIC_DISCORD_AUTH_URL=https://discord.com/api/oauth2/authorize?client_id=815294711983112194&redirect_uri=https%3A%2F%2Fapi-staging.devlaunchers.org%2Fusers%2Fauth%2Fdiscord%2Fcallback&response_type=code&scope=identify \ No newline at end of file +NEXT_PUBLIC_API_BASE_URL=https://apiv4-staging.devlaunchers.org +NEXT_PUBLIC_API_URL=https://apiv4-staging.devlaunchers.org/api +NEXT_PUBLIC_STRAPI_URL=https://apiv4-staging.devlaunchers.org/api +NEXT_PUBLIC_GOOGLE_AUTH_URL=https://apiv4-staging.devlaunchers.org/api/connect/google +NEXT_PUBLIC_DISCORD_AUTH_URL=https://discord.com/api/oauth2/authorize?client_id=815294711983112194&redirect_uri=https%3A%2F%2Fapi-staging.devlaunchers.org%2Fusers%2Fauth%2Fdiscord%2Fcallback&response_type=code&scope=identify +NEXT_PUBLIC_FRONT_END_URL=https://staging.devlaunchers.org \ No newline at end of file diff --git a/apps/app/.env.test b/apps/app/.env.test index 50456d86c..18271672f 100644 --- a/apps/app/.env.test +++ b/apps/app/.env.test @@ -1,5 +1,7 @@ NEXT_PUBLIC_NAME=STAGING +NEXT_PUBLIC_API_BASE_URL=https://api-staging.devlaunchers.org NEXT_PUBLIC_API_URL=https://api-staging.devlaunchers.org NEXT_PUBLIC_STRAPI_URL=https://api-staging.devlaunchers.org NEXT_PUBLIC_GOOGLE_AUTH_URL=https://api-staging.devlaunchers.org/connect/google -NEXT_PUBLIC_DISCORD_AUTH_URL=https://discord.com/api/oauth2/authorize?client_id=815294711983112194&redirect_uri=https%3A%2F%2Fapi-staging.devlaunchers.org%2Fusers%2Fauth%2Fdiscord%2Fcallback&response_type=code&scope=identify \ No newline at end of file +NEXT_PUBLIC_DISCORD_AUTH_URL=https://discord.com/api/oauth2/authorize?client_id=815294711983112194&redirect_uri=https%3A%2F%2Fapi-staging.devlaunchers.org%2Fusers%2Fauth%2Fdiscord%2Fcallback&response_type=code&scope=identify +NEXT_PUBLIC_FRONT_END_URL=http://localhost:3000 \ No newline at end of file diff --git a/apps/app/next.config.js b/apps/app/next.config.js index 43dd7e2b3..554458a2e 100644 --- a/apps/app/next.config.js +++ b/apps/app/next.config.js @@ -3,9 +3,11 @@ const withPlugins = require('next-compose-plugins'); const imagesPlugin = require('next-optimized-images'); const withTM = require('next-transpile-modules')([ '@devlaunchers/ideaspace', + '@devlaunchers/user-profile', '@devlaunchers/site-projects', '@devlaunchers/dev-recruiters', '@devlaunchers/website', + '@devlaunchers/gptbot', ]); // pass the modules you would like to see transpiled /** @type {import('next').NextConfig} */ @@ -35,18 +37,24 @@ const nextConfig = { domains: [ 'images.prismic.io', 'devlaunchersproduction.blob.core.windows.net', + 'devlaunchersstaging.blob.core.windows.net', 'lh3.googleusercontent.com', + 'localhost', + 'apiv4-staging.devlaunchers.org', ], disableStaticImages: true, - unoptimized: false + unoptimized: false, }, webpack: ( config, { buildId, dev, isServer, defaultLoaders, nextRuntime, webpack } ) => { // Important: return the modified config - config.resolve.alias['styled-components'] = path.resolve("./node_modules", "styled-components"); - return config + config.resolve.alias['styled-components'] = path.resolve( + './node_modules', + 'styled-components' + ); + return config; }, reactStrictMode: true, // It helps you avoid legacy code, and deprecated APIs. eslint: { diff --git a/apps/app/package.json b/apps/app/package.json index 430667268..8c46c8b01 100644 --- a/apps/app/package.json +++ b/apps/app/package.json @@ -7,8 +7,11 @@ "dependencies": { "@builder.io/partytown": "0.7.4", "@devlaunchers/dev-recruiters": "workspace:^", + "@devlaunchers/gptbot": "workspace:*", "@devlaunchers/ideaspace": "*", "@devlaunchers/site-projects": "*", + "@devlaunchers/tailwind": "workspace:*", + "@devlaunchers/user-profile": "workspace:*", "@devlaunchers/website": "*", "@next/env": "^12.2.3", "@ramonak/react-progress-bar": "^5.0.2", @@ -17,6 +20,7 @@ "autosave": "^1.0.3", "axios": "^0.27.2", "constate": "^3.3.2", + "formik": "^2.2.9", "luxon": "^3.0.1", "next": "^12.2.3", "next-compose-plugins": "^2.2.1", @@ -42,7 +46,7 @@ "styled-components": "5.3.5", "styled-normalize": "^8.0.7", "tabletop": "1.6.3", - "wrangler": "2.14.0" + "wrangler": "^3.0.1" }, "devDependencies": { "@devlaunchers/eslint-config-bases": "workspace:^", @@ -53,8 +57,6 @@ "eslint-config-airbnb-base": "^15.0.0", "eslint-config-next": "^12.2.3", "eslint-config-prettier": "^8.5.0", - "husky": "^8.0.1", - "lint-staged": "^13.0.3", "postcss": "^8.4.14", "prettier": "^2.7.1", "typescript": "^4.7.4", @@ -82,10 +84,6 @@ "not ie <= 11", "not op_mini all" ], - "lint-staged": { - "*.js": "eslint '*/**/*.{js,jsx}' --fix", - "*.{js,css,md}": "prettier --write" - }, "resolutions": { "styled-components": "^5" }, diff --git a/apps/app/pages/_app.js b/apps/app/pages/_app.js index 70dac4520..614120181 100644 --- a/apps/app/pages/_app.js +++ b/apps/app/pages/_app.js @@ -1,6 +1,9 @@ import Head from 'next/head'; -import { Footer, Navigation } from '@devlaunchers/components/components/organisms'; +import { + Footer, + Navigation, +} from '@devlaunchers/components/components/organisms'; import { UserDataProvider } from '@devlaunchers/components/context/UserDataContext'; import { initGA, @@ -13,6 +16,7 @@ import { ThemeProvider } from 'styled-components'; import theme from '../styles/theme'; import Script from 'next/script'; import iubendaScript from '../scripts/iubendaScript'; +import '@devlaunchers/tailwind/tailwind.css'; const hashRedirect = (router) => { // Strip out hash from url (if any) so we can transition from HashRouter to BrowserRouter @@ -49,8 +53,15 @@ function MyApp({ Component, pageProps }) {
- + -
-
- - +
+ + {/* {props.children} */}
diff --git a/apps/app/pages/_document.js b/apps/app/pages/_document.js index a91f3bcbe..3bff454ac 100644 --- a/apps/app/pages/_document.js +++ b/apps/app/pages/_document.js @@ -1,7 +1,6 @@ import Document, { Head, Html, Main, NextScript } from 'next/document'; import { ServerStyleSheet } from 'styled-components'; - export default class MyDocument extends Document { render() { return ( diff --git a/apps/app/pages/gptbot/index.jsx b/apps/app/pages/gptbot/index.jsx new file mode 100644 index 000000000..bbcf04797 --- /dev/null +++ b/apps/app/pages/gptbot/index.jsx @@ -0,0 +1,8 @@ +import Page from '@devlaunchers/gptbot/src/pages/index'; +import App from '@devlaunchers/gptbot/src/pages/_app'; +//export { getStaticProps } from '@devlaunchers/website/src/pages/index'; + +///////////////////////////////////////// + +import { constructAppPage } from '../../utils/routingTools.js'; +export default constructAppPage(App, Page); diff --git a/apps/app/pages/join/[slug]/index.jsx b/apps/app/pages/join/[slug]/index.jsx index d6ed5de8f..c0c60ceb6 100644 --- a/apps/app/pages/join/[slug]/index.jsx +++ b/apps/app/pages/join/[slug]/index.jsx @@ -1,10 +1,9 @@ import Page from '@devlaunchers/dev-recruiters/src/pages/[slug]/index'; -import App from '@devlaunchers/dev-recruiters/src/pages/_app'; +import App from '@devlaunchers/dev-recruiters/src/pages/_app.jsx'; export { getStaticProps, getStaticPaths, } from '@devlaunchers/dev-recruiters/src/pages/[slug]/index'; - ///////////////////////////////////////// import { constructAppPage } from '../../../utils/routingTools.js'; diff --git a/apps/app/pages/join/confirmation.jsx b/apps/app/pages/join/confirmation.jsx index 5855bd8d4..80611c3f9 100644 --- a/apps/app/pages/join/confirmation.jsx +++ b/apps/app/pages/join/confirmation.jsx @@ -1,9 +1,9 @@ import Page from '@devlaunchers/dev-recruiters/src/pages/confirmation'; -import App from '@devlaunchers/dev-recruiters/src/pages/_app'; +//import App from '@devlaunchers/dev-recruiters/_app.js'; +import App from '../../../dev-recruiters/src/pages/_app'; export { getStaticProps } from '@devlaunchers/dev-recruiters/src/pages/confirmation'; ///////////////////////////////////////// import { constructAppPage } from '../../utils/routingTools.js'; export default constructAppPage(App, Page); - diff --git a/apps/app/pages/join/index.jsx b/apps/app/pages/join/index.jsx index 92a768098..2893369aa 100644 --- a/apps/app/pages/join/index.jsx +++ b/apps/app/pages/join/index.jsx @@ -1,9 +1,8 @@ import Page from '@devlaunchers/dev-recruiters/src/pages/index'; -import App from '@devlaunchers/dev-recruiters/src/pages/_app'; +//import App from '@devlaunchers/dev-recruiters/src/pages/_app'; +import App from '../../../dev-recruiters/src/pages/_app'; export { getStaticProps } from '@devlaunchers/dev-recruiters/src/pages/index'; ///////////////////////////////////////// - import { constructAppPage } from '../../utils/routingTools.js'; export default constructAppPage(App, Page); - diff --git a/apps/app/pages/onboarding.js b/apps/app/pages/onboarding.js new file mode 100644 index 000000000..5196287d9 --- /dev/null +++ b/apps/app/pages/onboarding.js @@ -0,0 +1,7 @@ +import Page from '@devlaunchers/user-profile/src/pages/onboarding'; +import App from '@devlaunchers/user-profile/src/pages/_app'; + +///////////////////////////////////////// + +import { constructAppPage } from '../utils/routingTools.js'; +export default constructAppPage(App, Page); diff --git a/apps/app/pages/projects/[slug].jsx b/apps/app/pages/projects/[slug].jsx index 2a8b8b77e..cb55211e4 100644 --- a/apps/app/pages/projects/[slug].jsx +++ b/apps/app/pages/projects/[slug].jsx @@ -1,8 +1,11 @@ import Page from '@devlaunchers/site-projects/src/pages/[slug]'; import App from '@devlaunchers/site-projects/src/pages/_app'; -export { getStaticPaths, getStaticProps } from '@devlaunchers/site-projects/src/pages/[slug]'; +export { + getStaticPaths, + getStaticProps, +} from '@devlaunchers/site-projects/src/pages/[slug]'; ///////////////////////////////////////// import { constructAppPage } from '../../utils/routingTools.js'; -export default constructAppPage(App, Page); \ No newline at end of file +export default constructAppPage(App, Page); diff --git a/apps/app/pages/users/[userId].js b/apps/app/pages/users/[userId].js new file mode 100644 index 000000000..ee5c23b5b --- /dev/null +++ b/apps/app/pages/users/[userId].js @@ -0,0 +1,8 @@ +import Page from '@devlaunchers/user-profile/src/pages/users/[userId].js'; +import App from '@devlaunchers/user-profile/src/pages/_app'; +export { getStaticPaths, getStaticProps} from '@devlaunchers/user-profile/src/pages/users/[userId].js' + +///////////////////////////////////////// + +import { constructAppPage } from '../../utils/routingTools.js'; +export default constructAppPage(App, Page); diff --git a/apps/app/pages/users/[username].js b/apps/app/pages/users/[username].js deleted file mode 100644 index c59acfecd..000000000 --- a/apps/app/pages/users/[username].js +++ /dev/null @@ -1,8 +0,0 @@ -import Page from '@devlaunchers/website/src/pages/users/[username]'; -import IdeaApp from '@devlaunchers/website/src/pages/_app'; -export { getStaticProps } from '@devlaunchers/website/src/pages/index'; - -///////////////////////////////////////// - -import { constructAppPage } from '../../utils/routingTools.js'; -export default constructAppPage(IdeaApp, Page); diff --git a/apps/app/pages/users/me.js b/apps/app/pages/users/me.js index 485fb370b..52413956b 100644 --- a/apps/app/pages/users/me.js +++ b/apps/app/pages/users/me.js @@ -1,6 +1,5 @@ -import Page from '@devlaunchers/website/src/pages/users/me'; -import IdeaApp from '@devlaunchers/website/src/pages/_app'; -export { getStaticProps } from '@devlaunchers/website/src/pages/index'; +import Page from '@devlaunchers/user-profile/src/pages/users/me'; +import IdeaApp from '@devlaunchers/user-profile/src/pages/_app'; ///////////////////////////////////////// diff --git a/apps/dev-recruiters/package.json b/apps/dev-recruiters/package.json index f03db5fc1..e5930063e 100644 --- a/apps/dev-recruiters/package.json +++ b/apps/dev-recruiters/package.json @@ -53,8 +53,6 @@ "eslint-plugin-jsx-a11y": "^6.6.1", "eslint-plugin-react": "^7.30.1", "eslint-plugin-react-hooks": "^4.6.0", - "husky": "^8.0.1", - "lint-staged": "^13.0.3", "prettier": "^2.7.1", "react-is": "^17.0.2", "semantic-release": "^19.0.3", @@ -79,10 +77,6 @@ "not ie <= 11", "not op_mini all" ], - "lint-staged": { - "*.js": "eslint '*/**/*.{js,jsx}' --fix", - "*.{js,css,md}": "prettier --write" - }, "resolutions": { "styled-components": "^5", "@types/react": "^17.0.30" diff --git a/apps/dev-recruiters/src/components/common/Button/index.js b/apps/dev-recruiters/src/components/common/Button/index.js deleted file mode 100644 index 81d50d5ee..000000000 --- a/apps/dev-recruiters/src/components/common/Button/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from "./Button.tsx"; diff --git a/apps/dev-recruiters/src/components/common/Calendar/Calendar.js b/apps/dev-recruiters/src/components/common/Calendar/Calendar.js deleted file mode 100644 index 61feab6f3..000000000 --- a/apps/dev-recruiters/src/components/common/Calendar/Calendar.js +++ /dev/null @@ -1,96 +0,0 @@ -import React, { useState } from "react"; -import axios from "axios"; -import { DateTime } from "luxon"; -import { Wrapper, Event, Day, WeekdayTitle } from "./StyledCalendar"; - -const Calendar = ({ URL }) => { - const [eventList, setEventList] = useState([]); - - const current = DateTime.now(); - const max = current.plus({ days: 7 }); - - /* gets the JSON data from google calendar and makes a new array with only the properties we need */ - - const makeRequest = () => { - axios - .get(URL) - .then((response) => { - const tempEventList = []; - response.data.items.forEach((entry) => { - const time = DateTime.fromISO(entry.start.dateTime, { - zone: entry.start.timeZone, - }).setZone(); - tempEventList.push({ - name: entry.summary, - time: time.toFormat("t"), - weekday: time.weekday, - }); - }); - - setEventList(tempEventList); - }) - .catch(() => {}); - }; - - React.useEffect(makeRequest, []); - - const weekdays = ["MON", "TUES", "WED", "THURS", "FRI", "SAT", "SUN"]; - const dates = []; - - /* stores the dates of the previous weekdays in the Array, then today, then the remaining days in this week */ - const createDate = () => { - const todayInt = current.weekday; - for (let i = 1; i < todayInt; i++) { - dates.push(max.minus({ days: todayInt - i }).day); - } - dates.push(current.day); - - for (let i = todayInt + 1; i < 8; i++) { - dates.push(current.plus({ days: i - todayInt }).day); - } - - return dates; - }; - const dateslist = createDate(); - - return ( - - {weekdays.map((day, i) => { - const date = dateslist[i]; - return ( - - -
{day}
- -
- {date} -
-
- {eventList.map(({ name, time, weekday }) => { - if (weekday === i + 1) { - return ( - - {name} -
- {time} -
- ); - } - return null; - })} - {eventList.filter(({ weekday }) => weekday === i + 1).length === - 0 ? ( -
- No events -
- ) : ( - "" - )} -
- ); - })} -
- ); -}; - -export default Calendar; diff --git a/apps/dev-recruiters/src/components/common/Calendar/StyledCalendar.js b/apps/dev-recruiters/src/components/common/Calendar/StyledCalendar.js deleted file mode 100644 index 6eade9ebb..000000000 --- a/apps/dev-recruiters/src/components/common/Calendar/StyledCalendar.js +++ /dev/null @@ -1,57 +0,0 @@ -import Styled from "styled-components"; - -import theme from "../../../styles/theme"; - -export const Wrapper = Styled.div` - display: flex; - grid-area: WeeksGlance; - flex-direction: row; - width: 100%; -background-color: #DAD8D9; - - @media (orientation: portrait) { - flex-direction: column; - } -;`; - -export const Day = Styled.div` - display: flex; - color: #1c1c1c; - font-weight: 500; - padding-bottom: .5rem; - flex-direction: column; - font-size: 20px; - background-color: transparent; - width: 15%; - - @media (orientation: portrait) { - width:100%; - } -`; - -export const Event = Styled.div` - display: flex; - flex-wrap: wrap; - overflow: hidden; - white-space: nowrap; - background: bisque; - color: #f0edee; - width: 98%; - background-color: #ff7f0e; - margin-top: 1rem; - font-size: .9rem; - justify-content: center; - text-align: center; - padding-top: .3rem; - padding-bottom: .3rem; - font-family: roboto; -`; - -export const WeekdayTitle = Styled.div` - font-family: ${theme.fonts.headline}; - background-color: #f0edee; - color: #1c1c1c; - font-weight: 500; - padding-left: .5rem; - -`; diff --git a/apps/dev-recruiters/src/components/common/Calendar/index.js b/apps/dev-recruiters/src/components/common/Calendar/index.js deleted file mode 100644 index 6c64b5c40..000000000 --- a/apps/dev-recruiters/src/components/common/Calendar/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from "./Calendar"; diff --git a/apps/dev-recruiters/src/components/common/CardGroup/StyledCardGroup.tsx b/apps/dev-recruiters/src/components/common/CardGroup/StyledCardGroup.tsx deleted file mode 100644 index 086f90eb6..000000000 --- a/apps/dev-recruiters/src/components/common/CardGroup/StyledCardGroup.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import styled from "styled-components"; - -interface StyledProps { - cardGroupFlexDirection?: string, - groupTitleAlignment?: string -} - -export const Collection = styled.div ` - width: 100%; - display: flex; - flex-direction: row; - justify-content: flex-start; - flex-wrap: wrap; - align-items: flex-start; - justify-content: ${({ cardGroupFlexDirection }) => - cardGroupFlexDirection || "flex-start"}; -`; - -export const CollectionTitle = styled.h3` - text-align: ${({ groupTitleAlignment }) => groupTitleAlignment || "left"}; - padding: 1%; - margin-bottom: 0; -`; diff --git a/apps/dev-recruiters/src/components/common/CardGroup/index.js b/apps/dev-recruiters/src/components/common/CardGroup/index.js deleted file mode 100644 index c2d65d0fd..000000000 --- a/apps/dev-recruiters/src/components/common/CardGroup/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from "./CardGroup.tsx"; diff --git a/apps/dev-recruiters/src/components/common/CardTag/CardTag.tsx b/apps/dev-recruiters/src/components/common/CardTag/CardTag.tsx deleted file mode 100644 index 10bcd02b2..000000000 --- a/apps/dev-recruiters/src/components/common/CardTag/CardTag.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import { CardTagContainer, CardTagProps } from "./StyledCardTag"; - -interface Props extends CardTagProps { - children: any; -} - -export default function CardTag({ children, ...props }: Props) { - return {children}; -} diff --git a/apps/dev-recruiters/src/components/common/CardTag/StyledCardTag.ts b/apps/dev-recruiters/src/components/common/CardTag/StyledCardTag.ts deleted file mode 100644 index 96fd1749e..000000000 --- a/apps/dev-recruiters/src/components/common/CardTag/StyledCardTag.ts +++ /dev/null @@ -1,19 +0,0 @@ -import styled from "styled-components"; - -export interface CardTagProps { - dark?: boolean; - uppercase?: boolean -} - -export const CardTagContainer = styled.div` - background: ${(props) => (props.dark ? "#454d58" : "transparent")}; - color: ${(props) => (props.dark ? "#C4C4C4" : "#454d58")}; - font-family: ${({ theme }) => theme?.fonts?.normal}, sans-serif; - font-size: 1.12rem; - padding: .25rem 1.5rem; - border: 1px solid #454d58; - text-transform: ${(props) => (props.uppercase ? "uppercase" : "none")}; - box-sizing: border-box; - box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.25); - border-radius: 2.5rem; -`; \ No newline at end of file diff --git a/apps/dev-recruiters/src/components/common/Dropdown/DropdownButton.js b/apps/dev-recruiters/src/components/common/Dropdown/DropdownButton.js deleted file mode 100644 index 14ddc0a73..000000000 --- a/apps/dev-recruiters/src/components/common/Dropdown/DropdownButton.js +++ /dev/null @@ -1,42 +0,0 @@ -import React, { useEffect, useState, useRef } from "react"; -import { Wrapper, Toggle, Rooms } from "./StyledDropdownButton"; - -const DropdownButton = ({ toggleBtnText, dropdownItems, className }) => { - const [menuOpen, setMenuOpen] = useState(false); - const node = useRef(); - - const handleClickOutside = (e) => { - if (node.current.contains(e.target)) return; - // outside click - setMenuOpen(false); - }; - - useEffect(() => { - if (menuOpen) { - document.addEventListener("mousedown", handleClickOutside); - } else { - document.removeEventListener("mousedown", handleClickOutside); - } - - return () => { - document.removeEventListener("mousedown", handleClickOutside); - }; - }, [menuOpen]); - - return ( - - setMenuOpen(!menuOpen)} - as="button" - fontSize="1.2rem" - > - {toggleBtnText} - - - <>{dropdownItems} - - - ); -}; - -export default DropdownButton; diff --git a/apps/dev-recruiters/src/components/common/Dropdown/StyledDropdownButton.js b/apps/dev-recruiters/src/components/common/Dropdown/StyledDropdownButton.js deleted file mode 100644 index cc34302ff..000000000 --- a/apps/dev-recruiters/src/components/common/Dropdown/StyledDropdownButton.js +++ /dev/null @@ -1,38 +0,0 @@ -import styled from "styled-components"; -import Button from "../Button"; - -export const Wrapper = styled.div` - position: relative; - display: inline-block; -`; -export const Toggle = styled(Button)` - padding-left: 2rem; - padding-right: 2rem; -`; -export const Rooms = styled.div` - display: ${({ isOpen }) => (isOpen ? "block" : "none")}; - position: absolute; - right: 50%; - left: -50%; - background-color: #f1f1f1; - min-width: 160px; - overflow: auto; - box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.2); - border: 1px solid black; - z-index: 1; - - & > * { - color: black; - border: 1px solid black; - padding: 12px 16px; - text-decoration: none; - text-align: center; - display: block; - - &:hover, - &:focus { - background-color: #ddd; - color: #000; - } - } -`; diff --git a/apps/dev-recruiters/src/components/common/Dropdown/index.js b/apps/dev-recruiters/src/components/common/Dropdown/index.js deleted file mode 100644 index d06e95af1..000000000 --- a/apps/dev-recruiters/src/components/common/Dropdown/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from "./DropdownButton"; diff --git a/apps/dev-recruiters/src/components/common/Footer/Footer.js b/apps/dev-recruiters/src/components/common/Footer/Footer.js deleted file mode 100644 index 580c177b8..000000000 --- a/apps/dev-recruiters/src/components/common/Footer/Footer.js +++ /dev/null @@ -1,75 +0,0 @@ -import React from 'react'; -import Link from 'next/link'; - -import { - Wrapper, - FooterLogo, - FooterNav, - NavEntry, - SocialMediaContainer, - SocialMediaLink, - OrgInfoArea, -} from './StyledFooter'; -import RandomQuote from './RandomQuote'; -import Newsletter from './Newsletter'; - -export default function Footer() { - return ( - - - - - - CREATE - - - - - LEARN - - - {/* } - - PLAY - - { */} - - - SUPPORT US - - - - - JOIN - - - - - - - - - - - - - - - - - - - - Terms of Service - - - | - - - Privacy Policy - {' '} - {'- | '} ©Dev Launchers, 2020. - - - ); -} diff --git a/apps/dev-recruiters/src/components/common/Footer/Newsletter/Newsletter.js b/apps/dev-recruiters/src/components/common/Footer/Newsletter/Newsletter.js deleted file mode 100644 index 178886275..000000000 --- a/apps/dev-recruiters/src/components/common/Footer/Newsletter/Newsletter.js +++ /dev/null @@ -1,240 +0,0 @@ -import React, { useState } from "react"; -import { withTheme } from "styled-components"; -import axios from "axios"; -import { - Row, - Col, - ThankYouMessage, - ErrorMessage, - Secret, -} from "./StyledNewsletter"; -import validateEmail from "@utils/ValidateEmail"; -import { env } from "@utils/EnvironmentVariables"; - -const NewsLetter = (props) => { - const [email, setEmail] = useState(""); - const [isCorrect, setIsCorrect] = useState(false); - const [check, setCheck] = useState(false); - const [secret, setSecret] = useState(false); - const [isEmailAlreadyUsed, setIsEmailAlreadyUsed] = useState(false); - - const EmailValidation = () => { - if (email === "password") { - setSecret(true); - } else if (!validateEmail(email)) { - setCheck(true); - } else { - axios - .post(`${env().STRAPI_URL}/newsletters`, { - email, - }) - .then(() => { - setIsCorrect(true); - }) - .catch(() => { - setIsEmailAlreadyUsed(true); - }); - } - }; - - const sendEmail = () => { - EmailValidation(email); - }; - - if (secret) { - return ( -
-

- →{" "} - - 😄 - {" "} - ← -

-
- ); - } - if (isCorrect) { - return ( -
- -

Thank You!

-
- -

You successfully signed up to our newsletter!

-
-
- ); - } - if (check) { - return ( -
-

- - ✉ - -   Sign up to our newsletter!  -

- - - setEmail(e.target.value)} - value={email} - type="email" - placeholder="Enter your email here!" - /> - - - - - - Please enter a valid email! -
- ); - } - if (isEmailAlreadyUsed) { - return ( -
-

- - ✉ - -   Sign up to our newsletter!  -

- - - setEmail(e.target.value)} - value={email} - type="email" - placeholder="Enter your email here!" - /> - - - - - - You already used this email before -
- ); - } - return ( -
-

- - ✉ - -   Sign up to our newsletter!  -

- - - setEmail(e.target.value)} - value={email} - type="email" - placeholder="Enter your email here!" - /> - - - - - -
- ); -}; - -export default withTheme(NewsLetter); diff --git a/apps/dev-recruiters/src/components/common/Footer/Newsletter/StyledNewsletter.js b/apps/dev-recruiters/src/components/common/Footer/Newsletter/StyledNewsletter.js deleted file mode 100644 index 32af99dfa..000000000 --- a/apps/dev-recruiters/src/components/common/Footer/Newsletter/StyledNewsletter.js +++ /dev/null @@ -1,156 +0,0 @@ -import styled, { keyframes } from "styled-components"; - -const fadeIn = keyframes` - from { - opacity: 0; - } - to { - opacity: 1; - } -`; - -export const Row = styled.div` - display: flex; -`; - -export const Col = styled.div` - font-family: "Nunito Sans"; - flex: ${props => props.size}; - color: ${props => props.theme.colors.NEUTRAL_1}; - background-color: ${props => props.theme.colors.ACCENT_4}; - transition: 0.5s; - border: 5px solid ${props => props.theme.colors.NEUTRAL_1}; - margin: 0 auto; - padding: 10px; - text-align: center; - margin-bottom: 30px; - - #userEmail { - border: 0; - background: transparent; - outline: none; - font-weight: bold; - width: 96%; - text-align: center; - border-right: none; - box-shadow: 0px 0px 0px -7px ${props => props.theme.colors.NEUTRAL_1}, - 0px 0px 0px -7px ${props => props.theme.colors.NEUTRAL_1}; - -webkit-transition: box-shadow 0.5s; - transition: box-shadow 0.5s ease-in-out; - } - - #userEmail::placeholder { - color: ${props => props.theme.colors.NEUTRAL_1}; - transition: opacity 1s; - opacity: 1; - } - - #userEmail:hover::placeholder { - color: ${props => props.theme.colors.NEUTRAL_1}; - opacity: 0.5; - } - - #userEmail:focus::placeholder { - color: ${props => props.theme.colors.NEUTRAL_1}; - opacity: 0.5; - } - - #userEmail:hover { - box-shadow: 0px 10px 0px -7px ${props => props.theme.colors.NEUTRAL_1}, - 0px 10px 0px -7px ${props => props.theme.colors.NEUTRAL_1}; - } - - #userEmail:focus { - box-shadow: 0px 10px 0px -7px ${props => props.theme.colors.NEUTRAL_1}, - 0px 10px 0px -7px ${props => props.theme.colors.NEUTRAL_1}; - } - - #submitButton { - background: transparent; - transition: 0.3s; - text-decoration: none; - text-align: center; - border: none; - outline: none; - font-weight: bolder; - height: 1.75rem; - width: 100%; - box-sizing: border-box; - display: block; - cursor: pointer; - letter-spacing: 0.025em; - } - - :hover #submitButton { - text-shadow: black 0px 0px 1px; - color: ${props => props.theme.colors.ACCENT_4}; - } - - :hover { - background-color: ${props => props.theme.colors.ACCENT_3}; - } -`; - -export const ThankYouMessage = styled.div` - font-family: "Nunito Sans"; - margin-top: 30px; - text-align: center; - animation: float 3s infinite; - - @keyframes float { - 0% { - transform: translateY(0px); - } - 50% { - transform: translateY(-20px); - } - 100% { - transform: translateY(0px); - } - } - - #thankYou { - display: inline; - font-size: 3.5rem; - font-weight: bolder; - background: linear-gradient( - to right, - hsl(28, 100%, 53%), - hsl(45, 100%, 53%), - hsl(194, 52%, 67%), - hsl(203, 48%, 44%) - ); - -webkit-background-clip: text; - -webkit-text-fill-color: transparent; - } - - #message { - margin-top: -20px; - font-size: 1.75rem; - font-weight: bolder; - color: ${props => props.theme.colors.NEUTRAL_2}; - } -`; - -export const ErrorMessage = styled.p` - color: red; - font-family: "Nunito Sans"; - font-weight: bolder; - font-size: 1.25rem; - margin-top: -20px; - margin-bottom: 20px; - animation: ${fadeIn} 0.5s linear; -`; - -export const Secret = styled.a` - font-size: 7rem; - text-align: center; - - :hover span { - display: none; - } - - :hover:before { - content: "🤫"; - } -`; diff --git a/apps/dev-recruiters/src/components/common/Footer/Newsletter/index.js b/apps/dev-recruiters/src/components/common/Footer/Newsletter/index.js deleted file mode 100644 index d34ac646c..000000000 --- a/apps/dev-recruiters/src/components/common/Footer/Newsletter/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from "./Newsletter"; diff --git a/apps/dev-recruiters/src/components/common/Footer/RandomQuote/RandomQuote.js b/apps/dev-recruiters/src/components/common/Footer/RandomQuote/RandomQuote.js deleted file mode 100644 index b9eac8bd9..000000000 --- a/apps/dev-recruiters/src/components/common/Footer/RandomQuote/RandomQuote.js +++ /dev/null @@ -1,16 +0,0 @@ -import React, { useEffect, useState } from 'react'; -import Wrapper from './StyledRandomQuote'; - -// Retrieve all quotes from our content layer -const quotes = require('../../../../content/collections/quotes.json').data; - -export default function RandomQuote() { - const [quote, setQuote] = useState(''); - - useEffect(() => { - const randomQuote = quotes[parseInt(quotes.length * Math.random(), 10)]; - setQuote(randomQuote); - }, []); - - return {`"${quote.body}" - ${quote.author}`}; -} diff --git a/apps/dev-recruiters/src/components/common/Footer/RandomQuote/StyledRandomQuote.js b/apps/dev-recruiters/src/components/common/Footer/RandomQuote/StyledRandomQuote.js deleted file mode 100644 index ed320f3fb..000000000 --- a/apps/dev-recruiters/src/components/common/Footer/RandomQuote/StyledRandomQuote.js +++ /dev/null @@ -1,12 +0,0 @@ -import styled from "styled-components"; - -const Wrapper = styled.div` - text-align: center; - width: 60%; - padding-top: 0.5rem; - - @media (orientation: portrait) { - font-size: 3vw; - } -`; -export default Wrapper; diff --git a/apps/dev-recruiters/src/components/common/Footer/RandomQuote/index.js b/apps/dev-recruiters/src/components/common/Footer/RandomQuote/index.js deleted file mode 100644 index c8e325cdb..000000000 --- a/apps/dev-recruiters/src/components/common/Footer/RandomQuote/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from "./RandomQuote"; diff --git a/apps/dev-recruiters/src/components/common/Footer/StyledFooter.js b/apps/dev-recruiters/src/components/common/Footer/StyledFooter.js deleted file mode 100644 index 66448bebe..000000000 --- a/apps/dev-recruiters/src/components/common/Footer/StyledFooter.js +++ /dev/null @@ -1,126 +0,0 @@ -import styled from "styled-components"; -import logoMonogramImage from "@images/logo-monogram.png?webp"; - -function getRandomThemeColor(theme) { - const colors = [ - theme.colors.ACCENT_1, - theme.colors.ACCENT_2, - theme.colors.ACCENT_3, - theme.colors.ACCENT_4, - ]; - let colorTrack = -1; - - return () => { - colorTrack = colorTrack >= colors.length - 1 ? 0 : colorTrack + 1; - return colors[colorTrack]; - }; -} - -export const Wrapper = styled.div` - position: relative; - background-color: #1c1c1c; - color: #d9d9d9; - padding: 2rem 0 1rem 0; - display: flex; - flex-direction: column; - align-items: center; - gap: 2rem; - box-shadow: 0 3px 6px rgba(0, 0, 0, 0.16), 0 3px 6px rgba(0, 0, 0, 0.23); -`; - -export const FooterLogo = styled.img.attrs(() => ({ - src: logoMonogramImage, - alt: "logo", -}))` - width: 7rem; - margin-right: 10px; - margin-left: 10px; - margin-top: 10px; - margin-bottom: -10px; - transition: 0.3s; - - :hover { - transform: rotate(-60deg); - } -`; - -export const FooterNav = styled.div` - display: flex; - flex-wrap: wrap; - width: 35%; - justify-content: space-around; - row-gap: 1rem; - text-align: center; -`; -export const SocialMediaContainer = styled.div` - display: grid; - grid-template-columns: 1fr 1fr; - gap: 1rem; -`; - -export const SocialMediaLink = styled.a.attrs(({ Type }) => { - let href; - let className; - switch (Type) { - case "Instagram": - href = "https://www.instagram.com/devlaunchers/"; - className = "fab fa-instagram"; - break; - case "Linkedin": - href = "https://www.linkedin.com/company/devlaunchers/"; - className = "fab fa-linkedin"; - break; - case "Twitch": - href = "https://www.twitch.com/devlaunchers/"; - className = "fab fa-twitch"; - break; - case "Discord": - href = "https://www.discord.io/devlaunchers/"; - className = "fab fa-discord"; - break; - default: - href = null; - className = null; - break; - } - return { href, className }; -})` - font-size: 5rem; - color: ${({ theme }) => theme.colors.ACCENT_4}; - - transition: 0.3s; - :hover { - color: ${({ theme }) => theme.colors.NEUTRAL_2}; - transform: scale(1.25); - } -`; - -export const NavEntry = styled.div` - cursor: pointer; - font-size: 1.75rem; - border-bottom: 0.4rem solid transparent; - padding-top: 0.3rem; - padding-left: 0.5rem; - padding-right: 0.5rem; - transition: 0.5s; - - color: ${({ theme }) => theme.colors.NEUTRAL_2}; - - &:hover { - color: ${({ theme }) => theme.colors.ACCENT_4}; - border-bottom: 0.4rem solid ${({ theme }) => getRandomThemeColor(theme)}; - } - - @media (orientation: portrait) { - font-size: 1.2rem; - } -`; - -export const OrgInfoArea = styled.div` - width: 90%; - text-align: right; - font-size: 0.85rem; - display: flex; - flex-direction: row; - justify-content: flex-end; -`; diff --git a/apps/dev-recruiters/src/components/common/Footer/index.js b/apps/dev-recruiters/src/components/common/Footer/index.js deleted file mode 100644 index 3738288b0..000000000 --- a/apps/dev-recruiters/src/components/common/Footer/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from "./Footer"; diff --git a/apps/dev-recruiters/src/components/common/HamburgerMenu/HamburgerMenu.module.css b/apps/dev-recruiters/src/components/common/HamburgerMenu/HamburgerMenu.module.css deleted file mode 100644 index 0136757f2..000000000 --- a/apps/dev-recruiters/src/components/common/HamburgerMenu/HamburgerMenu.module.css +++ /dev/null @@ -1,74 +0,0 @@ -.burgerButton { - position: fixed; - min-height: 5vh; - min-width: 5vh; - right: 1.5vh; - top: 1.5vh; - - display: none; /* Only display on mobile */ -} - -.menuBody { - top: 0; -} -.overlay { - top: 0px; - right: 0px; -} -.burgerBar { - background: #d9d9d9; -} - -.crossClass { - background: #d9d9d9; -} - -.Logo { - margin-top: 20%; - width: 50%; -} -.Logo a { - display: inline-flex; - flex-direction: row; - flex-wrap: wrap; - align-items: center; -} -.LogoWrapper { - display: inline; - width: auto; -} -.LogoWrapper:hover { - filter: brightness(0) saturate(100%) invert(88%) sepia(10%) saturate(6102%) - hue-rotate(358deg) brightness(101%) contrast(103%); -} -.LogoImageHolder { - width: 100%; - margin: 2%; - margin-bottom: 0; -} -.LogoImage { - display: inline-block; - width: 100%; -} -.LogoWords { - position: absolute; - bottom: 5%; - text-align: center; - width: 100%; - font-family: "Abel", sans-serif; - font-size: 1.25rem; - margin-left: 0.25rem; - line-height: 140%; -} - -.navEntry { - text-align: center; - width: 100%; - margin-top: 10%; -} - -@media (orientation: portrait) { - .burgerButton { - display: block; - } -} diff --git a/apps/dev-recruiters/src/components/common/HamburgerMenu/HamburgerMenu.tsx b/apps/dev-recruiters/src/components/common/HamburgerMenu/HamburgerMenu.tsx deleted file mode 100644 index ff54f1bd7..000000000 --- a/apps/dev-recruiters/src/components/common/HamburgerMenu/HamburgerMenu.tsx +++ /dev/null @@ -1,137 +0,0 @@ -// eslint-disable-next-line no-use-before-define -import React from "react"; -import Link from "next/link"; -import Image from "next/image"; -import { slide as SlideHamburgerMenu } from "react-burger-menu"; -import style from "./HamburgerMenu.module.css"; -import logoMonogramImage from "@images/logo-monogram.png?webp"; -import Logout from "@utils/Logout"; -import { useUserDataContext } from "@contexts/UserDataContext"; - -const HamburgerMenu: React.FC = () => { - const { userData } = useUserDataContext(); - const [menuOpen, setMenuOpen] = React.useState(false); - - // Called when the open/close state of the menu changes (onStateChange callback) - const isMenuOpen = (state: { isOpen: boolean }) => { - setMenuOpen(state.isOpen); - }; - - // Called whenever a navigation item in the menu is clicked (closes menu) - function handleNavClick(): void { - setMenuOpen(false); - } - - return ( - -
-
-
- - -
- Logo -
-
- -
-
-
-
- - -
CREATE
-
- - - -
LEARN
-
- - -
- {userData.id ? ( - <> - - -
VISIT ACCOUNT PAGE
-
- - -
LOG OUT
-
- - ) : ( - -
SIGN IN
-
- )} -
- - {/* } - -
PLAY
- - { */} - - -
EARN
-
- - - -
SUPPORT US
-
- - - -
JOIN
-
- -
- {/* } -
- {authUser ? ( - - ) : ( - - )} -
- { */} -
- {/* }
Dev Launchers
*/} -
-
- ); -}; - -export default HamburgerMenu; diff --git a/apps/dev-recruiters/src/components/common/HamburgerMenu/index.js b/apps/dev-recruiters/src/components/common/HamburgerMenu/index.js deleted file mode 100644 index 5bb09ee01..000000000 --- a/apps/dev-recruiters/src/components/common/HamburgerMenu/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from "./HamburgerMenu.tsx"; diff --git a/apps/dev-recruiters/src/components/common/Header/AccountDropdown/AccountDropdown.js b/apps/dev-recruiters/src/components/common/Header/AccountDropdown/AccountDropdown.js index 6cfe5f1ba..6308da2af 100644 --- a/apps/dev-recruiters/src/components/common/Header/AccountDropdown/AccountDropdown.js +++ b/apps/dev-recruiters/src/components/common/Header/AccountDropdown/AccountDropdown.js @@ -1,17 +1,39 @@ import React from "react"; import Link from "next/link"; import Logout from "@utils/Logout"; -import { env } from "@utils/EnvironmentVariables"; import { AccountMenuDropdownItem, AccountMenuDropdownButton, MenuButton, } from "./StyledAccountDropdown"; +import { useUserDataContext } from "@devlaunchers/components/src/context/UserDataContext"; export default function AccountDropdown(props) { + const { userData, setUserData } = useUserDataContext(); + + const handleLogout = () => { + Logout(); + setUserData({ + id: 0, + name: '', + username: '', + email: '', + bio: '', + profilePictureUrl: '', + socialMediaLinks: [], + discord: { + id: 0, + avatar: '', + username: '', + discriminator: '', + }, + interests: [], + }) + }; + return (
- {props.userData.id ? ( + {userData.id ? ( Visit Account Page - + Logout{" "} } > ) : ( - + Sign In{" "} )} diff --git a/apps/dev-recruiters/src/components/common/Header/AccountDropdown/StyledAccountDropdown.js b/apps/dev-recruiters/src/components/common/Header/AccountDropdown/StyledAccountDropdown.js deleted file mode 100644 index 8e08734cd..000000000 --- a/apps/dev-recruiters/src/components/common/Header/AccountDropdown/StyledAccountDropdown.js +++ /dev/null @@ -1,31 +0,0 @@ -import styled from "styled-components"; -import DropdownButton from "../../Dropdown"; -import Button from "../../Button"; - -export const AccountMenuDropdownButton = styled(DropdownButton)` - @media (orientation: portrait) { - display: none; - } -`; - -export const MenuButton = styled(Button)` - @media (orientation: portrait) { - display: none; - } -`; - -export const AccountMenuDropdownItem = styled.a` - font-family: ${({ theme }) => theme.fonts.headline}, sans-serif; - background-color: #1c1c1c; - - width: ${({ width }) => width || ""}; - border: 0px; - - cursor: pointer; - font-size: ${({ fontSize }) => fontSize || "1.1rem"}; - margin-top: ${({ marginTop }) => marginTop || ""}; - padding: 0.5rem; - padding-left: 1rem; - padding-right: 1rem; - font-weight: bolder; -`; diff --git a/apps/dev-recruiters/src/components/common/Header/AccountDropdown/index.js b/apps/dev-recruiters/src/components/common/Header/AccountDropdown/index.js deleted file mode 100644 index ff7b7ab66..000000000 --- a/apps/dev-recruiters/src/components/common/Header/AccountDropdown/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from "./AccountDropdown"; diff --git a/apps/dev-recruiters/src/components/common/Header/Header.js b/apps/dev-recruiters/src/components/common/Header/Header.js deleted file mode 100644 index 658036150..000000000 --- a/apps/dev-recruiters/src/components/common/Header/Header.js +++ /dev/null @@ -1,68 +0,0 @@ -import React from "react"; -import Link from "next/link"; -import AccountDropdown from "./AccountDropdown"; - -import { - HeaderBlock, - Logo, - LogoWrapper, - LogoImageHolder, - LogoImage, - LogoWords, - HeaderNav, - NavEntry, -} from "./StyledHeader"; - -import { useUserDataContext } from "@contexts/UserDataContext"; -import HamburgerMenu from "../HamburgerMenu"; - -export default function Header() { - const { userData } = useUserDataContext(); - - return ( - - - - - - - - - Dev Launchers - - - - - - - - CREATE - - - - - LEARN - - - {/* } - - PLAY - - { */} - - - SUPPORT US - - - - - JOIN - - - -
- - - - ); -} diff --git a/apps/dev-recruiters/src/components/common/Header/StyledHeader.js b/apps/dev-recruiters/src/components/common/Header/StyledHeader.js deleted file mode 100644 index cb3749973..000000000 --- a/apps/dev-recruiters/src/components/common/Header/StyledHeader.js +++ /dev/null @@ -1,121 +0,0 @@ -import styled from "styled-components"; -import logoMonogramImage from "@images/logo-monogram.png?webp"; - -export const HeaderBlock = styled.div` - background-color: #1c1c1c; - min-height: 7.5vh; - width: 100%; - position: sticky; - top: 0; - z-index: 2; - - display: flex; - justify-content: space-between; - align-items: center; - - /* box-shadow: 0 3px 6px rgba(0, 0, 0, 0.16), 0 3px 6px rgba(0, 0, 0, 0.23); */ - - a { - color: ${({ theme }) => theme.colors.NEUTRAL_2}; - } -`; -export const Logo = styled.div` - width: 30%; - - a { - display: inline-flex; - flex-direction: row; - flex-wrap: nowrap; - align-items: center; - } -`; - -export const LogoWrapper = styled.div` - display: inline; - width: auto; - transition: 1s; - - &:hover { - filter: brightness(0) saturate(100%) invert(88%) sepia(10%) saturate(6102%) - hue-rotate(358deg) brightness(101%) contrast(103%); - } -`; - -export const LogoImageHolder = styled.div` - width: 3rem; - margin: 2%; - margin-bottom: 0; - @media (orientation: portrait) { - height: auto; - width: 4rem; - margin-left: 1vw; - } -`; -export const LogoImage = styled.img.attrs(() => ({ - src: logoMonogramImage, - alt: "logo", -}))` - display: inline-block; - width: 100%; -`; -export const LogoWords = styled.div` - width: 15rem; - font-family: "Abel", sans-serif; - font-size: 1.75rem; - margin-left: 0.25rem; - line-height: 140%; - @media (orientation: portrait) { - display: none; - } -`; - -export const HeaderNav = styled.div` - display: flex; - flex-direction: row; - justify-content: space-between; - font-size: 1rem; - width: 40%; - min-width: 300px; - height: 7.5vh; - align-items: center; - transition: 0.5s; - - & > a { - display: flex; - height: 100%; - align-items: center; - } - - @media (orientation: portrait) { - display: none; - } -`; - -export const NavEntry = styled.div` - font-family: "Abel", sans-serif; - cursor: pointer; -`; - -export const SignInArea = styled.div` - width: 10%; - text-align: center; - display: flex; - justify-content: space-around; - align-items: center; - @media (orientation: portrait) { - display: none; - } -`; -export const UserProfilePic = styled.img.attrs(() => ({ - alt: "profile-pic", -}))` - max-height: 3rem; - min-height: 2rem; - height: 5vw; - cursor: pointer; -`; -export const HeaderPusher = styled.div` - position: "relative"; - width: "100%"; - min-height: "7.5vh"; -`; diff --git a/apps/dev-recruiters/src/components/common/Header/index.js b/apps/dev-recruiters/src/components/common/Header/index.js deleted file mode 100644 index 2764567d9..000000000 --- a/apps/dev-recruiters/src/components/common/Header/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from "./Header"; diff --git a/apps/dev-recruiters/src/components/common/MultiRangeDropdown/MultiRangeDropDown.tsx b/apps/dev-recruiters/src/components/common/MultiRangeDropdown/MultiRangeDropDown.tsx deleted file mode 100644 index 9f2bc6eaa..000000000 --- a/apps/dev-recruiters/src/components/common/MultiRangeDropdown/MultiRangeDropDown.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import React from 'react'; -import AppDropDown from '../AppDropDown'; -import MultiRangeSlider from '../MultiRangeSlider'; - -interface Props { - title: string; - min: number; - max: number; - onChange: (value: any) => void; -} - -export default function MultiRangeDropDown({ title, ...props }: Props) { - return ( - - - - ); -} diff --git a/apps/dev-recruiters/src/components/common/MultiRangeDropdown/index.ts b/apps/dev-recruiters/src/components/common/MultiRangeDropdown/index.ts deleted file mode 100644 index c487b7423..000000000 --- a/apps/dev-recruiters/src/components/common/MultiRangeDropdown/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { default } from "./MultiRangeDropDown" \ No newline at end of file diff --git a/apps/dev-recruiters/src/components/common/MultiRangeSlider/MultiRangeSlider.tsx b/apps/dev-recruiters/src/components/common/MultiRangeSlider/MultiRangeSlider.tsx deleted file mode 100644 index dbea948e9..000000000 --- a/apps/dev-recruiters/src/components/common/MultiRangeSlider/MultiRangeSlider.tsx +++ /dev/null @@ -1,101 +0,0 @@ -import React, { useCallback, useEffect, useState, useRef } from 'react'; -import { - Container, - ThumbLeft, - ThumbRight, - Slider, - SliderTrack, - SliderRange, - SliderLeftValue, - SliderRightValue, -} from './styledMultiRangeSlider'; - -interface Props { - min: number; - max: number; - onChange: (value: any) => void; -} - -export default function MultiRangeSlider({ min, max, onChange }: Props) { - const [minVal, setMinVal] = useState(min); - const [maxVal, setMaxVal] = useState(max); - const minValRef = useRef(null); - const maxValRef = useRef(null); - const range = useRef(null); - - // Convert to percentage - const getPercent = useCallback( - (value: number) => Math.round(((value - min) / (max - min)) * 100), - [min, max] - ); - - // Set width of the range to decrease from the left side - useEffect(() => { - if (maxValRef.current) { - const minPercent = getPercent(minVal); - const maxPercent = getPercent(+maxValRef.current.value); // Precede with '+' to convert the value from type string to type number - - if (range.current) { - range.current.style.left = `${minPercent}%`; - range.current.style.width = `${maxPercent - minPercent}%`; - } - } - }, [minVal, getPercent]); - - // Set width of the range to decrease from the right side - useEffect(() => { - if (minValRef.current) { - const minPercent = getPercent(+minValRef.current.value); - const maxPercent = getPercent(maxVal); - - if (range.current) { - range.current.style.width = `${maxPercent - minPercent}%`; - } - } - }, [maxVal, getPercent]); - - // Get min and max values when their state changes - // useEffect(() => { - // onChange({ min: minVal, max: maxVal }); - // }, [minVal, maxVal, onChange]); - - return ( - - { - const value = Math.min(+event.target.value, maxVal - 1); - setMinVal(value); - event.target.value = value.toString(); - onChange({ min: value, max: maxVal }); - }} - /> - { - const value = Math.max(+event.target.value, minVal + 1); - setMaxVal(value); - event.target.value = value.toString(); - onChange({ min: minVal, max: value }); - }} - /> - - - - - {minVal} Hours - {maxVal} Hours - - - ); -} diff --git a/apps/dev-recruiters/src/components/common/MultiRangeSlider/index.ts b/apps/dev-recruiters/src/components/common/MultiRangeSlider/index.ts deleted file mode 100644 index 53f7e1c75..000000000 --- a/apps/dev-recruiters/src/components/common/MultiRangeSlider/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { default } from './MultiRangeSlider' \ No newline at end of file diff --git a/apps/dev-recruiters/src/components/common/MultiRangeSlider/styledMultiRangeSlider.ts b/apps/dev-recruiters/src/components/common/MultiRangeSlider/styledMultiRangeSlider.ts deleted file mode 100644 index bcc173dca..000000000 --- a/apps/dev-recruiters/src/components/common/MultiRangeSlider/styledMultiRangeSlider.ts +++ /dev/null @@ -1,129 +0,0 @@ -import styled from 'styled-components'; - -export const Container = styled.div` - font-family: ${({ theme }) => theme?.fonts?.normal}, sans-serif; - margin-top: 34px; - position: relative; - display: flex; - align-items: flex-start; - justify-content: center; - height: 100%; - width: 100%; - /* min-height: 4.5rem; */ -`; - -export const Slider = styled.div` - position: relative; - width: 12.5rem; -`; - -export const SliderTrack = styled.div` - position: absolute; - border-radius: 3px; - height: 5px; - background-color: #5b6068; - width: 100%; - z-index: 1; -`; - -export const SliderRange = styled.div` - position: absolute; - border-radius: 3px; - height: 5px; - background-color: #fff; - z-index: 2; -`; - -export const SliderLeftValue = styled.div` - position: absolute; - color: #dee2e6; - font-size: 20px; - margin-top: 20px; - left: 6px; -`; - -export const SliderRightValue = styled.div` - position: absolute; - color: #dee2e6; - font-size: 20px; - margin-top: 20px; - right: -4px; -`; - -export const ThumbRight = styled.input.attrs({ type: 'range' })` - pointer-events: none; - position: absolute; - height: 0; - width: 200px; - outline: none; - z-index: 4; - -webkit-appearance: none; - -webkit-tap-highlight-color: transparent; - - &::-webkit-slider-thumb { - -webkit-appearance: none; - -webkit-tap-highlight-color: transparent; - background: #59687b; - border: none; - border-radius: 50%; - box-shadow: 0 0 1px 1px #ced4da; - cursor: pointer; - height: 1.8125rem; - width: 1.8125rem; - margin-top: 4px; - pointer-events: all; - position: relative; - } - &::-moz-range-thumb { - background: #59687b; - color: yellow; - border: none; - border-radius: 50%; - box-shadow: 0 0 1px 1px #ced4da; - cursor: pointer; - height: 1.8125rem; - width: 1.8125rem; - margin-top: 4px; - pointer-events: all; - position: relative; - -webkit-appearance: none; - } -`; - -export const ThumbLeft = styled.input<{ minVal: number; maxVal: number }>` - pointer-events: none; - position: absolute; - height: 0; - width: 12.5rem; - outline: none; - -webkit-appearance: none; - -webkit-tap-highlight-color: transparent; - z-index: ${(props) => (props.minVal > props.maxVal - 100 ? 5 : 3)}; - &::-webkit-slider-thumb { - -webkit-appearance: none; - -webkit-tap-highlight-color: transparent; - background: #59687b; - border: none; - border-radius: 50%; - box-shadow: 0 0 1px 1px #ced4da; - cursor: pointer; - height: 1.8125rem; - width: 1.8125rem; - margin-top: 4px; - pointer-events: all; - position: relative; - } - - &::-moz-range-thumb { - background: #59687b; - border: none; - border-radius: 50%; - box-shadow: 0 0 1px 1px #ced4da; - cursor: pointer; - height: 1.8125rem; - width: 1.8125rem; - margin-top: 4px; - pointer-events: all; - position: relative; - } -`; diff --git a/apps/dev-recruiters/src/components/common/PageWrapper/PageWrapper.tsx b/apps/dev-recruiters/src/components/common/PageWrapper/PageWrapper.tsx deleted file mode 100644 index f4df4f355..000000000 --- a/apps/dev-recruiters/src/components/common/PageWrapper/PageWrapper.tsx +++ /dev/null @@ -1,43 +0,0 @@ -import styled from 'styled-components'; - -interface Props { - children: React.ReactElement; -} -export default function PageWrapper({ children }: Props) { - return ( - - {children} - - ); -} -const Wrapper = styled.div` - box-sizing: border-box; - background-color: #454d58; - - @media (max-width: 640px) { - padding: 0 16px; - } -`; - -const Container = styled.div` - box-sizing: border-box; - width: 100%; - margin: auto auto; - padding-left: 1.25rem; - padding-right: 1.25rem; - @media (min-width: 640px) { - max-width: 640px; - } - @media (min-width: 768px) { - max-width: 768px; - } - @media (min-width: 1024px) { - max-width: 1024px; - } - @media (min-width: 1280px) { - max-width: 1280px; - } - @media (min-width: 1536px) { - max-width: 1536px; - } -`; diff --git a/apps/dev-recruiters/src/components/common/PageWrapper/index.ts b/apps/dev-recruiters/src/components/common/PageWrapper/index.ts deleted file mode 100644 index 6dac36d90..000000000 --- a/apps/dev-recruiters/src/components/common/PageWrapper/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { default } from './PageWrapper' \ No newline at end of file diff --git a/apps/dev-recruiters/src/components/common/RainbowBar/RainbowBar.tsx b/apps/dev-recruiters/src/components/common/RainbowBar/RainbowBar.tsx deleted file mode 100644 index 7e96dca6a..000000000 --- a/apps/dev-recruiters/src/components/common/RainbowBar/RainbowBar.tsx +++ /dev/null @@ -1,26 +0,0 @@ -// eslint-disable-next-line no-use-before-define -import React from "react"; -import { useTheme } from "styled-components"; -// eslint-disable-next-line import/extensions -import { Wrapper, BarSection } from "./StyledRainbowBar"; - -interface IRainbowBar { - width: string; - height: string; -} -const RainbowBar: React.FC = ({ - width = "100%", - height = ".5rem", -}) => { - const theme = useTheme(); - return ( - - - - - - - ); -}; - -export default RainbowBar; diff --git a/apps/dev-recruiters/src/components/common/RainbowBar/StyledRainbowBar.tsx b/apps/dev-recruiters/src/components/common/RainbowBar/StyledRainbowBar.tsx deleted file mode 100644 index 88d70ac04..000000000 --- a/apps/dev-recruiters/src/components/common/RainbowBar/StyledRainbowBar.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import styled from "styled-components"; - -interface Props { - width?: string; - height?: string; - color?: string; -} -export const Wrapper = styled.div` - width: ${(props) => props.width}; - height: ${(props) => props.height}; - display: flex; - flex-direction: row; - flex-basis: auto; - align-items: stretch; -`; - -export const BarSection = styled.div` - width: 33.333333%; - background-color: ${(props) => props.color}; -`; diff --git a/apps/dev-recruiters/src/components/common/RainbowBar/index.js b/apps/dev-recruiters/src/components/common/RainbowBar/index.js deleted file mode 100644 index 04c18ea70..000000000 --- a/apps/dev-recruiters/src/components/common/RainbowBar/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from "./RainbowBar.tsx"; diff --git a/apps/dev-recruiters/src/components/common/RoundedButton/RoundedButton.tsx b/apps/dev-recruiters/src/components/common/RoundedButton/RoundedButton.tsx deleted file mode 100644 index 6249c2944..000000000 --- a/apps/dev-recruiters/src/components/common/RoundedButton/RoundedButton.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import { AppRoundedButton, RoundedButtonProps } from "./StyledRoundedButton"; - -interface Props extends RoundedButtonProps { - children: any; -} - -export default function RoundedButton({ children, ...props }: Props) { - return {children}; -} diff --git a/apps/dev-recruiters/src/components/common/RoundedButton/StyledRoundedButton.ts b/apps/dev-recruiters/src/components/common/RoundedButton/StyledRoundedButton.ts deleted file mode 100644 index 0c9d76d23..000000000 --- a/apps/dev-recruiters/src/components/common/RoundedButton/StyledRoundedButton.ts +++ /dev/null @@ -1,29 +0,0 @@ -import styled from "styled-components"; - - -export interface RoundedButtonProps { - fontSize?: number; - bgColor?: string; - textColor?: string; - borderRadius?: number; - paddingX?: number; - paddingY?: number; -} - -export const AppRoundedButton = styled.button` - justify-self: center; - width: auto; - font-family: ${({ theme }) => theme?.fonts?.normal}, sans-serif; - font-size: ${(props) => - props.fontSize ? `${props.fontSize}rem` : "1.12rem"}; - background: ${(props) => props.bgColor ?? "rgba(84, 87, 91, 0.2)"}; - color: ${(props) => props.textColor ?? "inherit"}; - border-radius: ${(props) => props.borderRadius ?? "1.25rem"}; - border: none; - padding-top: ${(props) => (props.paddingY ? `${props.paddingY}rem` : "1.25rem")}; - padding-bottom: ${(props) => - props.paddingY ? `${props.paddingY}rem` : ".62rem"}; - padding-left: ${(props) => (props.paddingX ? `${props.paddingX}rem` : "auto")}; - padding-right: ${(props) => - props.paddingX ? `${props.paddingX}rem` : "auto"}; -`; \ No newline at end of file diff --git a/apps/dev-recruiters/src/components/common/RoundedButton/index.ts b/apps/dev-recruiters/src/components/common/RoundedButton/index.ts deleted file mode 100644 index 34664b878..000000000 --- a/apps/dev-recruiters/src/components/common/RoundedButton/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { default } from "./RoundedButton" \ No newline at end of file diff --git a/apps/dev-recruiters/src/components/common/Section/Section.tsx b/apps/dev-recruiters/src/components/common/Section/Section.tsx deleted file mode 100644 index bd5716472..000000000 --- a/apps/dev-recruiters/src/components/common/Section/Section.tsx +++ /dev/null @@ -1,26 +0,0 @@ -// eslint-disable-next-line no-use-before-define -import React from "react"; -import CardGroup from "../CardGroup"; -// eslint-disable-next-line import/extensions -import { Group, Wrapper } from "./StyledSection"; - -interface ISection { - title: string; - data?: object; -} -const Section: React.FC = (props) => ( - -

{props.title}

-
- {Object.keys(props.data).map((groupTitle, i) => { - const group: string = props.data[groupTitle]; - return ( - - - - ); - })} -
-
-); -export default Section; diff --git a/apps/dev-recruiters/src/components/common/Section/StyledSection.tsx b/apps/dev-recruiters/src/components/common/Section/StyledSection.tsx deleted file mode 100644 index 9c3fca16b..000000000 --- a/apps/dev-recruiters/src/components/common/Section/StyledSection.tsx +++ /dev/null @@ -1,16 +0,0 @@ -import styled from "styled-components"; - -export const Wrapper = styled.div` - border-left: 1px solid black; - margin-bottom: 10%; - - h2 { - margin-left: 2%; - margin-bottom: 0; - } -`; - -export const Group = styled.div` - border-left: 1px solid black; - margin-left: 2%; -`; diff --git a/apps/dev-recruiters/src/components/common/Section/index.js b/apps/dev-recruiters/src/components/common/Section/index.js deleted file mode 100644 index 7f1968c58..000000000 --- a/apps/dev-recruiters/src/components/common/Section/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from "./Section.tsx"; diff --git a/apps/dev-recruiters/src/components/common/SignUpForm/FormEntry/FormEntry.js b/apps/dev-recruiters/src/components/common/SignUpForm/FormEntry/FormEntry.js deleted file mode 100644 index e93f71bb4..000000000 --- a/apps/dev-recruiters/src/components/common/SignUpForm/FormEntry/FormEntry.js +++ /dev/null @@ -1,20 +0,0 @@ -import React from "react"; - -import { FormEntryHolder, FormEntryTitle } from "./StyledFormEntry"; - -export default function FormEntry(props) { - return ( - - - - ); -} diff --git a/apps/dev-recruiters/src/components/common/SignUpForm/FormEntry/StyledFormEntry.js b/apps/dev-recruiters/src/components/common/SignUpForm/FormEntry/StyledFormEntry.js deleted file mode 100644 index 82aecad4f..000000000 --- a/apps/dev-recruiters/src/components/common/SignUpForm/FormEntry/StyledFormEntry.js +++ /dev/null @@ -1,17 +0,0 @@ -import styled from "styled-components"; - -export const FormEntryHolder = styled.div` - margin: 5%; - font-size: 1.5rem; - margin: 3%; - padding: 2%; - padding-bottom: 4%; -`; - -export const FormEntryTitle = styled.div` - width: 85%; - text-align: left; - margin-left: auto; - margin-right: auto; - font-size: 2.5rem; -`; diff --git a/apps/dev-recruiters/src/components/common/SignUpForm/FormEntry/index.js b/apps/dev-recruiters/src/components/common/SignUpForm/FormEntry/index.js deleted file mode 100644 index 4c467497c..000000000 --- a/apps/dev-recruiters/src/components/common/SignUpForm/FormEntry/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from "./FormEntry"; diff --git a/apps/dev-recruiters/src/components/common/SignUpForm/InputAdder/InputAdder.js b/apps/dev-recruiters/src/components/common/SignUpForm/InputAdder/InputAdder.js deleted file mode 100644 index 1ce3ec454..000000000 --- a/apps/dev-recruiters/src/components/common/SignUpForm/InputAdder/InputAdder.js +++ /dev/null @@ -1,31 +0,0 @@ -import React, { useState } from "react"; - -const InputAdder = (props) => { - const [item, setItem] = useState(""); - return ( -
- { - e.preventDefault(); - setItem(e.target.value); - }} - /> - -
- ); -}; - -InputAdder.displayName = "InputAdder"; - -export default InputAdder; diff --git a/apps/dev-recruiters/src/components/common/SignUpForm/InputAdder/index.js b/apps/dev-recruiters/src/components/common/SignUpForm/InputAdder/index.js deleted file mode 100644 index f5dbf0557..000000000 --- a/apps/dev-recruiters/src/components/common/SignUpForm/InputAdder/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from "./InputAdder"; diff --git a/apps/dev-recruiters/src/components/common/SignUpForm/InputField/InputField.js b/apps/dev-recruiters/src/components/common/SignUpForm/InputField/InputField.js deleted file mode 100644 index 44efe580c..000000000 --- a/apps/dev-recruiters/src/components/common/SignUpForm/InputField/InputField.js +++ /dev/null @@ -1,47 +0,0 @@ -import React from "react"; - -import { useField, splitFormProps } from "react-form"; - -import { FormInput } from "./StyledInputField"; - -const InputField = React.forwardRef((props, ref) => { - // Let's use splitFormProps to get form-specific props - const [field, fieldOptions, rest] = splitFormProps(props); - - // Use the useField hook with a field and field options - // to access field state - const { - meta: { error, isTouched, isValidating, message }, - getInputProps, - } = useField(field, fieldOptions); - - const Validate = () => { - if (isValidating) { - return Validating...; - } - if (isTouched && error) { - return {error}; - } - if (message) { - return {message}; - } - return null; - }; - // Build the field - return ( - <> - -
- {/* - Let's inline some validation and error information - for our field - */} - - {Validate()} -
- - ); -}); - -InputField.displayName = "InputField"; -export default InputField; diff --git a/apps/dev-recruiters/src/components/common/SignUpForm/InputField/StyledInputField.js b/apps/dev-recruiters/src/components/common/SignUpForm/InputField/StyledInputField.js deleted file mode 100644 index febb19d72..000000000 --- a/apps/dev-recruiters/src/components/common/SignUpForm/InputField/StyledInputField.js +++ /dev/null @@ -1,15 +0,0 @@ -import styled from "styled-components"; - -export const FormInput = styled.input` - width: 100%; - font-size: 4rem; - color: white; - background-color: transparent; - outline: none; - border: none; - border-bottom: 2px solid white; - - @media (orientation: portrait) { - font-size: 2rem; - } -`; diff --git a/apps/dev-recruiters/src/components/common/SignUpForm/InputField/index.js b/apps/dev-recruiters/src/components/common/SignUpForm/InputField/index.js deleted file mode 100644 index f2820d0d9..000000000 --- a/apps/dev-recruiters/src/components/common/SignUpForm/InputField/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from "./InputField"; diff --git a/apps/dev-recruiters/src/components/common/SignUpForm/ProgressBar/ProgressBar.js b/apps/dev-recruiters/src/components/common/SignUpForm/ProgressBar/ProgressBar.js deleted file mode 100644 index 376802968..000000000 --- a/apps/dev-recruiters/src/components/common/SignUpForm/ProgressBar/ProgressBar.js +++ /dev/null @@ -1,11 +0,0 @@ -import React from "react"; - -import { BarBackground, BarForeground } from "./StyledProgressBar"; - -export default function ProgressBar(props) { - return ( - - - - ); -} diff --git a/apps/dev-recruiters/src/components/common/SignUpForm/ProgressBar/StyledProgressBar.js b/apps/dev-recruiters/src/components/common/SignUpForm/ProgressBar/StyledProgressBar.js deleted file mode 100644 index a9357453c..000000000 --- a/apps/dev-recruiters/src/components/common/SignUpForm/ProgressBar/StyledProgressBar.js +++ /dev/null @@ -1,12 +0,0 @@ -import styled from "styled-components"; - -export const BarBackground = styled.div` - width: 100%; - height: 2rem; - background-color: black; -`; - -export const BarForeground = styled.div` - height: 100%; - background-color: white; -`; diff --git a/apps/dev-recruiters/src/components/common/SignUpForm/ProgressBar/index.js b/apps/dev-recruiters/src/components/common/SignUpForm/ProgressBar/index.js deleted file mode 100644 index 7b773c6e1..000000000 --- a/apps/dev-recruiters/src/components/common/SignUpForm/ProgressBar/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from "./ProgressBar"; diff --git a/apps/dev-recruiters/src/components/common/SignUpForm/ScheduleOrientationButton/ScheduleOrientationButton.js b/apps/dev-recruiters/src/components/common/SignUpForm/ScheduleOrientationButton/ScheduleOrientationButton.js deleted file mode 100644 index bc9a9c9aa..000000000 --- a/apps/dev-recruiters/src/components/common/SignUpForm/ScheduleOrientationButton/ScheduleOrientationButton.js +++ /dev/null @@ -1,21 +0,0 @@ -import React from "react"; -import { openPopupWidget } from "react-calendly"; - -import Button from "../../Button"; - -export default function ScheduleOrientationButton(props) { - return ( - - ); -} diff --git a/apps/dev-recruiters/src/components/common/SignUpForm/ScheduleOrientationButton/index.js b/apps/dev-recruiters/src/components/common/SignUpForm/ScheduleOrientationButton/index.js deleted file mode 100644 index ae41ad928..000000000 --- a/apps/dev-recruiters/src/components/common/SignUpForm/ScheduleOrientationButton/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from "./ScheduleOrientationButton"; diff --git a/apps/dev-recruiters/src/components/common/SignUpForm/SelectField/SelectField.js b/apps/dev-recruiters/src/components/common/SignUpForm/SelectField/SelectField.js deleted file mode 100644 index e4d5f4254..000000000 --- a/apps/dev-recruiters/src/components/common/SignUpForm/SelectField/SelectField.js +++ /dev/null @@ -1,44 +0,0 @@ -import React from "react"; - -import { useField, splitFormProps } from "react-form"; - -export default function SelectField(props) { - const [field, fieldOptions, { options, ...rest }] = splitFormProps(props); - - const { - setValue, - form, - meta: { error, isTouched }, - } = useField(field, fieldOptions); - - const handleSelectChange = (e) => { - setValue(e.target.value); - }; - - const currentValue = - form.values[field] === "" || form.values[field].length === 0 - ? "none" - : form.values[field]; - - return ( -
- {" "} - {isTouched && error ? {error} : null} -
- ); -} diff --git a/apps/dev-recruiters/src/components/common/SignUpForm/SelectField/index.js b/apps/dev-recruiters/src/components/common/SignUpForm/SelectField/index.js deleted file mode 100644 index 7ab5ade68..000000000 --- a/apps/dev-recruiters/src/components/common/SignUpForm/SelectField/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from "./SelectField"; diff --git a/apps/dev-recruiters/src/components/common/SignUpForm/SignUpForm.js b/apps/dev-recruiters/src/components/common/SignUpForm/SignUpForm.js deleted file mode 100644 index 1cd38e601..000000000 --- a/apps/dev-recruiters/src/components/common/SignUpForm/SignUpForm.js +++ /dev/null @@ -1,336 +0,0 @@ -/* eslint-disable no-nested-ternary */ -import React from "react"; -import { useForm } from "react-form"; -import axios from "axios"; -// eslint-disable-next-line no-unused-vars - -import { FormOuter, FormContainer, StyledForm } from "./StyledSignUpForm"; - -import Button from "../Button"; - -import ProgressBar from "./ProgressBar"; -import FormEntry from "./FormEntry"; -import InputField from "./InputField"; -import SelectField from "./SelectField"; -import TextAreaField from "./TextAreaField"; -import { env } from "@utils/EnvironmentVariables"; -import InputAdder from "./InputAdder"; - -function validateEmail(email) { - const re = - /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; - return re.test(String(email).toLowerCase()); -} - -export default function SignUpForm(props) { - const [submittingForm, setSubmittingForm] = React.useState(false); - const [isFormValidated, setIsFormValidated] = React.useState(false); - const [formPage, setFormPage] = React.useState(0); - const incrementFormPage = () => { - setFormPage(formPage + 1); - }; - const decrementFormPage = () => { - setFormPage(formPage - 1); - }; - const [progressPercent, setProgressPercent] = React.useState(0); - const [formSubmitted, setFormSubmitted] = React.useState(false); - - const onSubmit = async (values) => { - if (!isFormValidated) { - // eslint-disable-next-line no-use-before-define - if (formPage < formEntries.length - 1) incrementFormPage(); - return; - } - const { - project, - name, - role, - email, - age, - skills, - level, - experience, - commitment, - reason, - accepted, - } = values; - - setSubmittingForm(true); - const axiosInstance = axios.create({ - baseURL: - "https://script.google.com/macros/s/AKfycby9cNYNtLoRg68F8KhibzBam0sonk0Q-h_qQke9qeep5vOw2zICKbBtxOcCCQSyNznHhA", - timeout: 10000, - headers: { "Content-Type": "multipart/form-data" }, - }); - axiosInstance - .get("/exec", { params: values }) - .then(() => { - // handle success - setSubmittingForm(false); - setFormSubmitted(true); - }) - .catch(() => { - // handle error - // console.log(response); - }); - axios - .post(`${env().STRAPI_URL}/applicants`, { - email, - name, - age, - role, - zip: 23, - experience, - commitment, - accepted: !!accepted, - reason, - project, - level: level.toLowerCase(), - skills: skills.map((skill) => ({ skill })), - }) - .then(() => { - setSubmittingForm(false); - setFormSubmitted(true); - }) - .catch(() => {}); - }; - - const defaultValues = React.useMemo( - () => ({ - project: props.projectSlug, - name: "", - role: props.roleName, - email: "", - age: "", - skills: [], - level: "", - experience: "", - commitment: 1, - reason: "", - accepted: "", - }), - [] - ); - - const { - Form, - values, - pushFieldValue, - removeFieldValue, - meta: { canSubmit }, - } = useForm({ - defaultValues, - validate: () => { - // Check if all fields have been filled - let isValidated = true; - Object.keys(values).forEach((key) => { - if (values[key] === "") { - isValidated = false; - } - }); - setIsFormValidated(isValidated); - return false; - }, - onSubmit, - debugForm: false, - }); - - const formEntries = [ - - (!value ? "Required" : false)} - /> - , - - { - if (!value) { - return "Email is required"; - } - - if (!validateEmail(value)) { - return "Please enter a valid email addresss"; - } - - // We're going to mock that for now - await new Promise((resolve) => setTimeout(resolve, 2000)); - - return value === "tanner@gmail.com" - ? "Email is already being used" - : false; - }} - /> - , - - - - value < 13 ? "You must be at least 13 years old" : false - } - min="1" - /> - , - - -
- My Skills: - {values.skills.map((skill, i) => ( -
- -
- ))} -
, - - (!value ? "This is required!" : false)} - /> - , - - - value < 1 ? "You must at least commit an hour per week" : false - } - min="1" - /> - , - - - , - - - , - - - (!value ? "This is required!" : false)} - /> - , - ]; - - // Update progressPercent when formPage changes - React.useEffect(() => { - setProgressPercent(((formPage * 1.0) / (formEntries.length - 1)) * 100); - }, [formPage]); - return ( - - {formSubmitted ? ( -
- Thanks for submitting your application! -
-
- ) : ( - -
- {formPage > 0 ? ( - - ) : ( - "" - )} - {/*
Join project!
*/} -
- - {formEntries[formPage]} - - {submittingForm ? ( - "Submitting your information..." - ) : formPage === formEntries.length - 1 ? ( - !isFormValidated ? ( - - It appears some form elements have not been filled out! - - ) : ( -
- -
- ) - ) : ( - "" - )} -
- - {formPage < formEntries.length - 1 ? ( - - ) : ( - "" - )} - {formPage > 0 ? ( - - ) : ( - "" - )} -
- )} -
- ); -} diff --git a/apps/dev-recruiters/src/components/common/SignUpForm/StyledSignUpForm.js b/apps/dev-recruiters/src/components/common/SignUpForm/StyledSignUpForm.js deleted file mode 100644 index 216b2dc98..000000000 --- a/apps/dev-recruiters/src/components/common/SignUpForm/StyledSignUpForm.js +++ /dev/null @@ -1,34 +0,0 @@ -import styled from "styled-components"; - -export const FormOuter = styled.div` - width: 100%; - padding-top: 2%; - padding-bottom: 2%; - background-size: cover; - background-position: center; - background-repeat: no-repeat; -`; - -export const FormContainer = styled.div` - /* width: 100%; - min-height: 60vh; */ - padding: 2%; - margin-left: auto; - margin-right: auto; - /* background-color: rgba(0, 0, 0, 0.75); */ - - display: flex; - flex-direction: column; - justify-content: space-around; - - @media (orientation: portrait) { - width: 90%; - } -`; - -export const StyledForm = styled.div` - background-color: transparent; - text-align: center; - font-size: 4rem; - color: white; -`; diff --git a/apps/dev-recruiters/src/components/common/SignUpForm/TextAreaField/StyledTextAreaField.js b/apps/dev-recruiters/src/components/common/SignUpForm/TextAreaField/StyledTextAreaField.js deleted file mode 100644 index 45fc42e5a..000000000 --- a/apps/dev-recruiters/src/components/common/SignUpForm/TextAreaField/StyledTextAreaField.js +++ /dev/null @@ -1,11 +0,0 @@ -import styled from "styled-components"; - -export const FormTextArea = styled.textarea` - width: 85%; - font-size: 2rem; - color: white; - background-color: transparent; - outline: none; - border: none; - border-bottom: 2px solid white; -`; diff --git a/apps/dev-recruiters/src/components/common/SignUpForm/TextAreaField/TextAreaField.js b/apps/dev-recruiters/src/components/common/SignUpForm/TextAreaField/TextAreaField.js deleted file mode 100644 index 4275cb2f7..000000000 --- a/apps/dev-recruiters/src/components/common/SignUpForm/TextAreaField/TextAreaField.js +++ /dev/null @@ -1,47 +0,0 @@ -import React from "react"; - -import { useField, splitFormProps } from "react-form"; - -import { FormTextArea } from "./StyledTextAreaField"; - -const TextAreaField = React.forwardRef((props, ref) => { - // Let's use splitFormProps to get form-specific props - const [field, fieldOptions, rest] = splitFormProps(props); - - // Use the useField hook with a field and field options - // to access field state - const { - meta: { error, isTouched, isValidating, message }, - getInputProps, - } = useField(field, fieldOptions); - - const validate = () => { - if (isValidating) { - return Validating...; - } - if (isTouched && error) { - return {error}; - } - if (message) { - return {message}; - } - return null; - }; - // Build the field - return ( - <> - -
- {/* - Let's inline some validation and error information - for our field - */} - - {validate()} -
- - ); -}); - -TextAreaField.displayName = "TextAreaField"; -export default TextAreaField; diff --git a/apps/dev-recruiters/src/components/common/SignUpForm/TextAreaField/index.js b/apps/dev-recruiters/src/components/common/SignUpForm/TextAreaField/index.js deleted file mode 100644 index b2bda6bb3..000000000 --- a/apps/dev-recruiters/src/components/common/SignUpForm/TextAreaField/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from "./TextAreaField"; diff --git a/apps/dev-recruiters/src/components/common/SignUpForm/index.js b/apps/dev-recruiters/src/components/common/SignUpForm/index.js deleted file mode 100644 index 603a95423..000000000 --- a/apps/dev-recruiters/src/components/common/SignUpForm/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from "./SignUpForm"; diff --git a/apps/dev-recruiters/src/components/common/TitledHeader/TitledHeader.tsx b/apps/dev-recruiters/src/components/common/TitledHeader/TitledHeader.tsx index 04720ae61..44812f852 100644 --- a/apps/dev-recruiters/src/components/common/TitledHeader/TitledHeader.tsx +++ b/apps/dev-recruiters/src/components/common/TitledHeader/TitledHeader.tsx @@ -15,24 +15,21 @@ const TitledHeader = ({ title, backButtonUrl }) => { const previousPath = router.asPath.slice(0, finalSlashIndex); return ( - + - + - - + + {title} diff --git a/apps/dev-recruiters/src/components/modules/DetailedPage/PositionCard/PositionCard.tsx b/apps/dev-recruiters/src/components/modules/DetailedPage/PositionCard/PositionCard.tsx index ac844c3c1..6cb079eb0 100644 --- a/apps/dev-recruiters/src/components/modules/DetailedPage/PositionCard/PositionCard.tsx +++ b/apps/dev-recruiters/src/components/modules/DetailedPage/PositionCard/PositionCard.tsx @@ -8,11 +8,11 @@ import SignUpForm from '../../FormPage/signUpForm'; import Modal from '../PositionPopupModal/Modal'; import { RowContainer } from '../styledProjectDetails'; import { - ApplyButton, + // ApplyButton, BulletList, BulletListItem, Button, - ButtonsSection, + // ButtonsSection, CloseButton, CloseIcon, ColorBox, @@ -36,14 +36,23 @@ import { TagsSection, TitleSection, } from './StyledPositionCard'; +import { + ApplyButton, + ButtonsSection, +} from '@components/modules/FilterPage/RolesFilterComponent/RolesFilterList/SearchRoles/RoleModal/StyledRoleModal'; interface Props { projectSlug: string; + projectId: string; position: Opportunity; handleCloseModal?: () => void; } -export default function PositionCard({ position, projectSlug }: Props) { +export default function PositionCard({ + position, + projectId, + projectSlug, +}: Props) { const [isExpanded, setIsExpanded] = useState(false); const [liked, setLiked] = useState(false); const [showModal, setShowModal] = useState(false); @@ -76,11 +85,11 @@ export default function PositionCard({ position, projectSlug }: Props) { -

{position.title}

+

{position.attributes.title}

-

{position.level}

-

{position.commitmentHoursPerWeek} hrs/week

+

{position.attributes.level}

+

{position.attributes.commitmentHoursPerWeek} hrs/week

Apply @@ -224,6 +239,7 @@ export default function PositionCard({ position, projectSlug }: Props) { function ProjectDetailsModal({ position, handleCloseModal, + projectId, projectSlug, }: Props) { return ( @@ -244,10 +260,15 @@ function ProjectDetailsModal({ /> - +
@@ -258,26 +279,28 @@ function ModalTopSection({ position }: Props) { return ( -

{position.title}

+

{position.attributes.title}

{/*

{position.isPlatform ? "Platform" : "Independent"}

*/}

PRODUCT PLATFORM

TIME COMMITMENT
-

{position.commitmentHoursPerWeek} hrs per week

+

{position.attributes.commitmentHoursPerWeek} hrs per week

ABOUT THE PROJECT

-

{position.description}

+

{position.attributes.description}

SKILLS REQUIRED

- {position.level} - {position?.skills?.map((skill, index) => ( + + {position.attributes.level} + + {/*{position?.skills?.map((skill, index) => ( {skill?.interest} - ))} + ))} */}
@@ -287,6 +310,7 @@ function ModalTopSection({ position }: Props) { function ModalBottomSection({ position, + projectId, projectSlug, handleCloseModal, }: Props) { @@ -315,11 +339,11 @@ function ModalBottomSection({
- {position.expectations.length > 0 && ( + {position.attributes.expectations.length > 0 && (

RESPONSIBILITIES

- {position.expectations.map((item, index) => ( + {position.attributes.expectations.map((item, index) => ( {item.expectation} @@ -340,7 +364,12 @@ function ModalBottomSection({ handleOpenModal={handleOpenApplyModal} closeModal={handleCloseApplyModal} modalContent={ - + } />
diff --git a/apps/dev-recruiters/src/components/modules/DetailedPage/ProductHeader/ProductHeader.tsx b/apps/dev-recruiters/src/components/modules/DetailedPage/ProductHeader/ProductHeader.tsx index 388acedd4..2f3fafff4 100644 --- a/apps/dev-recruiters/src/components/modules/DetailedPage/ProductHeader/ProductHeader.tsx +++ b/apps/dev-recruiters/src/components/modules/DetailedPage/ProductHeader/ProductHeader.tsx @@ -1,5 +1,5 @@ -import React from "react"; -import Avatar from "../../../../images/avatar.png"; +import React from 'react'; +import Avatar from '../../../../images/avatar.png'; import { HeaderBlock, ColumnTitle, @@ -12,15 +12,23 @@ import { Commitment, Vision, UsernameAvatar, -} from "./StyledProductHeader"; -import { Leader2, Project, Team } from "@devlaunchers/models/project"; - +} from './StyledProductHeader'; +import { Leader2, Project, Team } from '@devlaunchers/models/project'; +import { string } from 'yup'; +/* interface ProductHeaderProps extends Pick< Project, - "title" | "vision" | "isPlatform" | "interests" | "published_at" | "team" + 'title' | 'vision' | 'isPlatform' | 'interests' | 'published_at' | 'team' > { - type: "Product" | "Project" | "Idea"; + type: 'Product' | 'Project' | 'Idea'; + userAvatar?: string; + minCommitmentHours: number; + maxCommitmentHours: number; +} */ +/*interface ProductHeaderProps + extends Pick { + type: 'Product' | 'ProjectLite' | 'Idea'; userAvatar?: string; minCommitmentHours: number; maxCommitmentHours: number; @@ -29,38 +37,47 @@ interface ProductHeaderProps export default function ProductHeader({ title, vision, - isPlatform = false, + //isPlatform = false, type, - interests = [], //keywords = [], + //interests = [], //keywords = [], published_at, //date, - team, //username, + //team, //username, // userAvatar, minCommitmentHours, maxCommitmentHours, -}: ProductHeaderProps) { - const teamLeader = (team?.leaders[0] as Partial)?.username; - const formattedDate = new Date(published_at) +}: ProductHeaderProps) */ +interface ProductHeaderProps { + title: string; +} +export default function ProductHeader({ title }: ProductHeaderProps) { + //const teamLeader = (team?.leaders[0] as Partial)?.username; + /* const formattedDate = new Date(published_at as string) .toDateString() - .split(" ") + .split(' ') .slice(1) - .join(" "); + .join(' '); +*/ return ( {title} + {/*} - {isPlatform ? "Platform" : "Independent"} {type} + {isPlatform ? 'Platform' : 'Independent'} {type} - {vision} + */} + {title} Tags + {/* - {interests.map((interest, id) => ( - {interest.interest} - ))} + {interests.data.map(({ attributes, id }) => { + return {attributes.interest}; + })} + */} - - {type} Lead + + {title} Lead - {teamLeader} + {/* {teamLeader} */} - - Product Created: {formattedDate} + + Product Created: {'formattedDate'} - - {minCommitmentHours} - {maxCommitmentHours} hrs/week + + {'minCommitmentHours'} - {'maxCommitmentHours'} hrs/week diff --git a/apps/dev-recruiters/src/components/modules/DetailedPage/ProductHeader/index.js b/apps/dev-recruiters/src/components/modules/DetailedPage/ProductHeader/index.js index b6299f93f..9d39fcb51 100644 --- a/apps/dev-recruiters/src/components/modules/DetailedPage/ProductHeader/index.js +++ b/apps/dev-recruiters/src/components/modules/DetailedPage/ProductHeader/index.js @@ -1 +1 @@ -export {default} from "./ProductHeader"; \ No newline at end of file +export { default } from './ProductHeader'; diff --git a/apps/dev-recruiters/src/components/modules/DetailedPage/ProjectDetails.tsx b/apps/dev-recruiters/src/components/modules/DetailedPage/ProjectDetails.tsx index bf35dcfba..a063e9f36 100644 --- a/apps/dev-recruiters/src/components/modules/DetailedPage/ProjectDetails.tsx +++ b/apps/dev-recruiters/src/components/modules/DetailedPage/ProjectDetails.tsx @@ -2,7 +2,7 @@ import BoxContainer from '../../common/BoxContainer'; import { Opportunity } from '@devlaunchers/models/opportunity'; import Link from 'next/link'; import { useState } from 'react'; -import { Project } from 'src/models/project'; +import { Project } from '@devlaunchers/models'; import LongCard from '../OpportunitiesAggregator/LongCard'; import ShortCard from '../OpportunitiesAggregator/ShortCard'; import PositionCard from './PositionCard'; @@ -18,7 +18,7 @@ import { Wrapper, } from './styledProjectDetails'; -interface Props { +interface ProjectDetailsProps { project: Project; opportunites: Opportunity[]; maxCommitment: number; @@ -30,7 +30,7 @@ export default function ProjectDetails({ opportunites, maxCommitment, minCommitment, -}: Props) { +}: ProjectDetailsProps) { const [expanded, setExpanded] = useState([]); const IsExpanded = (id: string) => { @@ -44,7 +44,6 @@ export default function ProjectDetails({ setExpanded(items); }; - return ( @@ -68,21 +67,11 @@ export default function ProjectDetails({ - + @@ -92,11 +81,10 @@ export default function ProjectDetails({

{title}

- {elements - .slice(0, IsExpanded(title) ? elements.length : 1) - .map((element, elIndex) => ( -

{element}

- ))} + {!elements && + elements + .slice(0, IsExpanded(title) ? elements.length : 1) + .map((element, elIndex) =>

{element}

)} handleExpand(title)}> {expanded.some((x) => x === title) ? 'Collapse Description' diff --git a/apps/dev-recruiters/src/components/modules/DetailedPage/index.ts b/apps/dev-recruiters/src/components/modules/DetailedPage/index.ts index 5707e8e44..6cc1cb8c8 100644 --- a/apps/dev-recruiters/src/components/modules/DetailedPage/index.ts +++ b/apps/dev-recruiters/src/components/modules/DetailedPage/index.ts @@ -1 +1 @@ -export { default } from "./ProjectDetails" \ No newline at end of file +export { default } from './ProjectDetails'; diff --git a/apps/dev-recruiters/src/components/modules/FilterPage/RolesFilterComponent/RolesFilterList/CollapsibleContainerFilter/styles.ts b/apps/dev-recruiters/src/components/modules/FilterPage/RolesFilterComponent/RolesFilterList/CollapsibleContainerFilter/styles.ts index 5e92642a3..e474f4dbd 100644 --- a/apps/dev-recruiters/src/components/modules/FilterPage/RolesFilterComponent/RolesFilterList/CollapsibleContainerFilter/styles.ts +++ b/apps/dev-recruiters/src/components/modules/FilterPage/RolesFilterComponent/RolesFilterList/CollapsibleContainerFilter/styles.ts @@ -23,7 +23,7 @@ export const ContainerHead = styled.div` `; export const ContainerBody = styled.div` - height: 650px; + height: 700px; display: flex; flex-direction: column; justify-content: space-around; diff --git a/apps/dev-recruiters/src/components/modules/FilterPage/RolesFilterComponent/RolesFilterList/SearchRoles/RoleCard/index.tsx b/apps/dev-recruiters/src/components/modules/FilterPage/RolesFilterComponent/RolesFilterList/SearchRoles/RoleCard/index.tsx index de1910930..26045b5b2 100644 --- a/apps/dev-recruiters/src/components/modules/FilterPage/RolesFilterComponent/RolesFilterList/SearchRoles/RoleCard/index.tsx +++ b/apps/dev-recruiters/src/components/modules/FilterPage/RolesFilterComponent/RolesFilterList/SearchRoles/RoleCard/index.tsx @@ -13,8 +13,21 @@ import { TimeContainer, Title, } from './styledRoleCard'; +import { agent } from '@devlaunchers/utility'; +import { result } from 'lodash'; +import { Opportunity } from '@devlaunchers/models'; -const RoleCard = (props) => { +let oppProject = { + projectId: 'projectId', + projectSlug: 'projectSlug', +}; +interface Props { + role: any; + key22: number; + opportunities?: Opportunity[]; +} + +const RoleCard = ({ role, key22, opportunities }: Props) => { const [showModal, setShowModal] = useState(false); const handleOpenModal = () => { @@ -25,19 +38,38 @@ const RoleCard = (props) => { setShowModal(false); }; + /*const getProjectSlugs = async () => { + try { + const oppProjectSlug = await agent.Opportunities.getById( + `${props.role.id}` + ).then((result) => { + oppProject.projectId = result.attributes.projects.data[0].id; + oppProject.projectSlug = + result.attributes.projects.data[0].attributes.slug; + + return result; + }); + return oppProjectSlug; + } catch (error) { + console.log('an error occured while fetching project slugs'); + } + }; + + getProjectSlugs(); + */ return ( - {props.role.title} + {role.title} TIME COMMITMENT - + ABOUT THE ROLE - {props.role.description} + {role.description} More Details @@ -48,8 +80,11 @@ const RoleCard = (props) => { handleOpenModal={handleOpenModal} modalContent={ } diff --git a/apps/dev-recruiters/src/components/modules/FilterPage/RolesFilterComponent/RolesFilterList/SearchRoles/RoleModal/RoleDetailsModal.tsx b/apps/dev-recruiters/src/components/modules/FilterPage/RolesFilterComponent/RolesFilterList/SearchRoles/RoleModal/RoleDetailsModal.tsx index 07a13d852..03ce740fc 100644 --- a/apps/dev-recruiters/src/components/modules/FilterPage/RolesFilterComponent/RolesFilterList/SearchRoles/RoleModal/RoleDetailsModal.tsx +++ b/apps/dev-recruiters/src/components/modules/FilterPage/RolesFilterComponent/RolesFilterList/SearchRoles/RoleModal/RoleDetailsModal.tsx @@ -19,11 +19,19 @@ import { ApplyButton, ButtonsSection, CloseButton } from './StyledRoleModal'; interface Props { projectSlug: string; + projectId: string; position: Opportunity; handleCloseModal?: () => void; } -function RoleDetailsModal({ position, handleCloseModal, projectSlug }: Props) { +function RoleDetailsModal({ + position, + handleCloseModal, + projectId, + projectSlug, +}: Props) { + console.log(projectId); + console.log(projectSlug); return (
@@ -42,9 +50,14 @@ function RoleDetailsModal({ position, handleCloseModal, projectSlug }: Props) { /> - + @@ -56,26 +69,29 @@ function ModalTopSection({ position }: Props) { return ( -

{position.title}

+

{position.attributes.title}

{/*

{position.isPlatform ? "Platform" : "Independent"}

*/}

Product Platform

TIME COMMITMENT
-

{position.commitmentHoursPerWeek} hrs per week

+

{position.attributes.commitmentHoursPerWeek} hrs per week

ABOUT THE PROJECT

-

{position.description}

+

{position.attributes.description}

SKILLS REQUIRED

- {position.level} - {position?.skills?.map((skill, index) => ( + + {position.attributes.level} + + {/* } {position?.skills?.map((skill, index) => ( {skill?.interest} ))} + */}
@@ -85,6 +101,7 @@ function ModalTopSection({ position }: Props) { function ModalBottomSection({ position, + projectId, projectSlug, handleCloseModal, }: Props) { @@ -136,7 +153,12 @@ function ModalBottomSection({ handleOpenModal={handleOpenApplyModal} closeModal={handleCloseApplyModal} modalContent={ - + } />
diff --git a/apps/dev-recruiters/src/components/modules/FilterPage/RolesFilterComponent/RolesFilterList/SearchRoles/SuggestedRole/index.tsx b/apps/dev-recruiters/src/components/modules/FilterPage/RolesFilterComponent/RolesFilterList/SearchRoles/SuggestedRole/index.tsx index 6e920e0d7..ed8c67607 100644 --- a/apps/dev-recruiters/src/components/modules/FilterPage/RolesFilterComponent/RolesFilterList/SearchRoles/SuggestedRole/index.tsx +++ b/apps/dev-recruiters/src/components/modules/FilterPage/RolesFilterComponent/RolesFilterList/SearchRoles/SuggestedRole/index.tsx @@ -53,6 +53,7 @@ const SuggestedRole = () => { modalContent={ diff --git a/apps/dev-recruiters/src/components/modules/FilterPage/RolesFilterComponent/RolesFilterList/SearchRoles/index.tsx b/apps/dev-recruiters/src/components/modules/FilterPage/RolesFilterComponent/RolesFilterList/SearchRoles/index.tsx index da3570428..262a2b0dc 100644 --- a/apps/dev-recruiters/src/components/modules/FilterPage/RolesFilterComponent/RolesFilterList/SearchRoles/index.tsx +++ b/apps/dev-recruiters/src/components/modules/FilterPage/RolesFilterComponent/RolesFilterList/SearchRoles/index.tsx @@ -5,8 +5,13 @@ import { EmptyRolesContainer, OpenRolesText } from './styles'; import { RolesContainer } from './styles'; import SuggestedRole from './SuggestedRole'; import { useOpportunitiesContext } from '../../../../../../contexts/SelectRoleContext'; +import { Opportunity } from '@devlaunchers/models'; -function SearchRole({ selectedRoleLabel }) { +interface Props { + selectedRoleLabel: any; + opportunities?: Opportunity[]; +} +function SearchRole({ selectedRoleLabel, opportunities }: Props) { const [selectedRole, setSelectedRole] = useState([]); const { commitmentRange } = useOpportunitiesContext(); @@ -26,8 +31,6 @@ function SearchRole({ selectedRoleLabel }) { } else { setSelectedRole(selectedRoleLabel); } - console.log(selectedRoleLabel); - console.log(commitmentRange); }, [selectedRoleLabel, commitmentRange]); /* @@ -44,7 +47,6 @@ function SearchRole({ selectedRoleLabel }) { } }, [commitmentRange, selectedRoleLabel]); */ - return ( <> <> @@ -60,9 +62,15 @@ function SearchRole({ selectedRoleLabel }) { ) : ( - {selectedRole.map((role, index) => ( - - ))} + {selectedRole.map((role, index) => { + return ( + + ); + })} )} diff --git a/apps/dev-recruiters/src/components/modules/FilterPage/RolesFilterComponent/RolesFilterList/UseOpenPositions/index.tsx b/apps/dev-recruiters/src/components/modules/FilterPage/RolesFilterComponent/RolesFilterList/UseOpenPositions/index.tsx index 0f67954f3..1afe659d0 100644 --- a/apps/dev-recruiters/src/components/modules/FilterPage/RolesFilterComponent/RolesFilterList/UseOpenPositions/index.tsx +++ b/apps/dev-recruiters/src/components/modules/FilterPage/RolesFilterComponent/RolesFilterList/UseOpenPositions/index.tsx @@ -2,18 +2,21 @@ import { useEffect, useState } from 'react'; import { Opportunity } from '@devlaunchers/models/opportunity'; -import { separateRoles } from './opportunityUtils'; +import separateRoles from './opportunityUtils'; import { ProjectLite } from '@devlaunchers/models/project'; interface Props { projects?: ProjectLite[]; - projectsLoaded: boolean; + projectsLoaded?: boolean; + opportunities?: Opportunity[]; } -export function useOpenPositions(projects: ProjectLite[] | undefined) { - const allOpportunities = - projects?.flatMap((project) => project.opportunities) || []; - +export default function useOpenPositions({ + projects, + projectsLoaded, + opportunities, +}: Props) { + const allOpportunities = opportunities?.flatMap((opportun) => opportun) || []; const [openPositions, setOpenPositions] = useState<{ [key: string]: Opportunity[]; }>({ diff --git a/apps/dev-recruiters/src/components/modules/FilterPage/RolesFilterComponent/RolesFilterList/UseOpenPositions/opportunityUtils.tsx b/apps/dev-recruiters/src/components/modules/FilterPage/RolesFilterComponent/RolesFilterList/UseOpenPositions/opportunityUtils.tsx index 35f66786a..256d57ac9 100644 --- a/apps/dev-recruiters/src/components/modules/FilterPage/RolesFilterComponent/RolesFilterList/UseOpenPositions/opportunityUtils.tsx +++ b/apps/dev-recruiters/src/components/modules/FilterPage/RolesFilterComponent/RolesFilterList/UseOpenPositions/opportunityUtils.tsx @@ -1,8 +1,8 @@ -// OpportunityUtils.ts +// opportunityUtils.tsx import { Opportunity } from '@devlaunchers/models/opportunity'; -export function separateRoles(arr: Opportunity[]) { +export default function separateRoles(arr: Opportunity[]) { const separatedGroups: { [key: string]: Opportunity[] } = { ProductLead: [], UxDesigner: [], @@ -38,6 +38,5 @@ export function separateRoles(arr: Opportunity[]) { separatedGroups['QaTester'].push(role); } }); - return separatedGroups; } diff --git a/apps/dev-recruiters/src/components/modules/FilterPage/RolesFilterComponent/RolesFilterList/index.tsx b/apps/dev-recruiters/src/components/modules/FilterPage/RolesFilterComponent/RolesFilterList/index.tsx index a29b97fd2..a4c9088d7 100644 --- a/apps/dev-recruiters/src/components/modules/FilterPage/RolesFilterComponent/RolesFilterList/index.tsx +++ b/apps/dev-recruiters/src/components/modules/FilterPage/RolesFilterComponent/RolesFilterList/index.tsx @@ -9,39 +9,54 @@ import { RolesContainer, SearchResult, } from './styles'; -import { useOpenPositions } from './UseOpenPositions'; +import useOpenPositions from './UseOpenPositions'; +//import useOpenPositions from './UseOpenPositions'; +import { Opportunity } from '@devlaunchers/models'; interface Props { projects?: ProjectLite[]; projectsLoaded: boolean; + opportunities?: Opportunity[]; } -export default function RolesFilterList({ projects, projectsLoaded }: Props) { +export default function RolesFilterList({ + projects, + projectsLoaded, + opportunities, +}: Props) { if (!projectsLoaded) return
loading please wait
; const [selectRoleLabel, setSelectRoleLabel] = useState(null); - function handleRoleSelection(roleLabel) { + function handleRoleSelection(roleLabel: any) { setSelectRoleLabel(roleLabel); } - const openPositions = useOpenPositions(projects); - - return ( - - Search Results - - - - - - - - - - ); + const openPositions = useOpenPositions({ + projects, + projectsLoaded, + opportunities, + }); + { + return ( + + Search Results + + + + + + + + + + ); + } } diff --git a/apps/dev-recruiters/src/components/modules/FilterPage/RolesFilterComponent/RolesFilterList/styles.ts b/apps/dev-recruiters/src/components/modules/FilterPage/RolesFilterComponent/RolesFilterList/styles.ts index 816d676f5..2caedccbf 100644 --- a/apps/dev-recruiters/src/components/modules/FilterPage/RolesFilterComponent/RolesFilterList/styles.ts +++ b/apps/dev-recruiters/src/components/modules/FilterPage/RolesFilterComponent/RolesFilterList/styles.ts @@ -25,6 +25,10 @@ export const RolesContainer = styled.div` export const SearchResult = styled.div` width: 100%; + height: 100%; + position: relative; + background: rgba(0, 0, 0, 0.63); + backdrop-filter: blur(4px); display: flex; justify-content: center; font-weight: 700; diff --git a/apps/dev-recruiters/src/components/modules/FilterPage/RolesFilterComponent/index.tsx b/apps/dev-recruiters/src/components/modules/FilterPage/RolesFilterComponent/index.tsx index 9927ef0df..95238bec5 100644 --- a/apps/dev-recruiters/src/components/modules/FilterPage/RolesFilterComponent/index.tsx +++ b/apps/dev-recruiters/src/components/modules/FilterPage/RolesFilterComponent/index.tsx @@ -39,6 +39,7 @@ export default function FilterComponent({ )} diff --git a/apps/dev-recruiters/src/components/modules/FilterPage/index.tsx b/apps/dev-recruiters/src/components/modules/FilterPage/index.tsx index c039d81e9..3053468e1 100644 --- a/apps/dev-recruiters/src/components/modules/FilterPage/index.tsx +++ b/apps/dev-recruiters/src/components/modules/FilterPage/index.tsx @@ -4,12 +4,14 @@ import BoxContainer from '../../common/BoxContainer'; import { HeaderJoinPage } from '../NewJoinPageComponent/HeaderJoinPage'; import FilterComponent from './RolesFilterComponent'; +import { useRouter } from 'next/router'; import { Wrapper, Footer, FooterFirstText, FooterSecondText, BtnSignUp, + SearchResult, } from './styles'; export interface FilteringComponentProps { @@ -23,10 +25,16 @@ const FilterPageComponent: React.FunctionComponent = ({ projects, opportunities, }) => { + const router = useRouter(); + const routeChange = (e) => { + e.preventDefault(); + router.push('/join/second'); + }; return ( - + {/* */} + Search Results @@ -40,13 +48,7 @@ const FilterPageComponent: React.FunctionComponent = ({ Join the Dev Launchers Talent Community to be notified about new volunteering roles that match your skillset! - + Join the Talent Community diff --git a/apps/dev-recruiters/src/components/modules/FilterPage/styles.ts b/apps/dev-recruiters/src/components/modules/FilterPage/styles.ts index 5b34b1eb0..588de23d6 100644 --- a/apps/dev-recruiters/src/components/modules/FilterPage/styles.ts +++ b/apps/dev-recruiters/src/components/modules/FilterPage/styles.ts @@ -1,4 +1,5 @@ import styled from 'styled-components'; +import StarySky from '../../../images/Searchresults/starysky.jpg'; export const Wrapper = styled.div` background-color: ${({ theme }) => theme?.colors?.SilverSandT20}; @@ -37,3 +38,27 @@ export const BtnSignUp = styled.button` background-color: ${({ theme }) => theme?.colors?.BLUE_700}; text-decoration: none; `; + +export const SearchResult = styled.div` + width: 100%; + height: 172px; + position: relative; + /* background: rgba(0, 0, 0, 0.63); */ + background-image: linear-gradient( + 0deg, + rgba(0, 0, 0, 0.6) 0%, + rgba(0, 0, 0, 0.063) 100% + ), + url(${StarySky}); + backdrop-filter: blur(4px); + display: flex; + justify-content: center; + align-items: center; + text-align: center; + font-size: 44px; + font-family: Abel; + font-weight: 400; + word-wrap: break-word; + + color: white; +`; diff --git a/apps/dev-recruiters/src/components/modules/FormPage/index.tsx b/apps/dev-recruiters/src/components/modules/FormPage/index.tsx index 323179cdd..841ddc991 100644 --- a/apps/dev-recruiters/src/components/modules/FormPage/index.tsx +++ b/apps/dev-recruiters/src/components/modules/FormPage/index.tsx @@ -1 +1 @@ -export { default } from "./signUpForm"; +export { default } from './signUpForm'; diff --git a/apps/dev-recruiters/src/components/modules/FormPage/signUpForm.tsx b/apps/dev-recruiters/src/components/modules/FormPage/signUpForm.tsx index ac3e7dfc9..a16e08acb 100644 --- a/apps/dev-recruiters/src/components/modules/FormPage/signUpForm.tsx +++ b/apps/dev-recruiters/src/components/modules/FormPage/signUpForm.tsx @@ -1,5 +1,4 @@ import { atoms, organisms } from '@devlaunchers/components/src/components'; -import theme from '@devlaunchers/components/src/styles/theme'; import FormErrorScroller from '@devlaunchers/components/src/utils/formErrorScroller'; import { Opportunity } from '@devlaunchers/models'; import { NewApplicant } from '@devlaunchers/models/newApplicant'; @@ -7,6 +6,7 @@ import { agent } from '@devlaunchers/utility'; import { Field, Form, Formik, FormikHelpers } from 'formik'; import { useState } from 'react'; import { ThemeProvider } from 'styled-components'; +import theme from '@devlaunchers/dev-recruiters/src/styles/theme'; import * as Yup from 'yup'; import ConfirmationModal from '../DetailedPage/Confirmation/ConfirmationModal'; import { @@ -19,11 +19,18 @@ interface FormFields extends Omit { level: NewApplicant['level'] | ''; } interface Props { + projectId: string; + projectSlug: string; handleCloseModal: () => void; position: Opportunity; } -export default function SignUpForm({ handleCloseModal, position }: Props) { +export default function SignUpForm({ + projectId, + projectSlug, + handleCloseModal, + position, +}: Props) { const SignupSchema = Yup.object().shape({ name: Yup.string().required('Name Field Entry is Required'), email: Yup.string() @@ -40,6 +47,16 @@ export default function SignUpForm({ handleCloseModal, position }: Props) { commitment: Yup.number() .moreThan(4, 'Commitment Field Entry is Required') .required('Commitment Field Entry is Required'), + /* Adding new column yearsExperience column */ + yearsOfExperience: Yup.number() + .default(0) + .min(0, 'Years of Experience should be greater than 0') + .max(100, 'Years of Expereince should be less than 100') + .test( + 'maxDigitsAfterDecimal', + 'Years of Experience must have 2 digits after decimal or less', + (number) => /^\d+(\.\d{1,2})?$/.test(number.toString()) + ), experience: Yup.string().required('Experience Field Entry is Required'), accepted: Yup.boolean().required('Acceptance Field Entry is Required'), }); @@ -77,12 +94,12 @@ export default function SignUpForm({ handleCloseModal, position }: Props) { commitment: 0, extraInfo: '', portfolioLink: null, + yearsOfExperience: 0, experience: '', reason: '', zip: 0, role: 'title' as string, // role: position.title as string, - id: '5' as string, // id: position.id as string, - // project: router.query.slug as string, + project: { id: '1', slug: 'projectSlug' }, //router.query.slug as string }, skills: [{ skill: '' }], }} onSubmit={( @@ -98,12 +115,12 @@ export default function SignUpForm({ handleCloseModal, position }: Props) { .toString() .split(',') .map((skill) => ({ skill: skill })), - role: 'title' as string, // role: position.title as string, - // project: router.query.project as string, - id: '5' as string, // id: position.id as string, + role: position.attributes.title as string, + project: { id: projectId, slug: projectSlug }, //router.query.slug as string }, }) .then((res) => { - handleOpenConfirmationModal(); + console.log(res); + handleOpenConfirmationModal(); setSubmitting(false); }) .catch((error) => { @@ -136,9 +153,11 @@ export default function SignUpForm({ handleCloseModal, position }: Props) {
- + {/* is the atoms.Layer setting correct? */} + - Volunteer Application for {'title'}{' '} + Volunteer Application for{' '} + {position.attributes.title}{' '} {/*position.title */} @@ -208,12 +227,23 @@ export default function SignUpForm({ handleCloseModal, position }: Props) { {errors.commitment} + +
Dev Launchers Volunteer Application
- - diff --git a/apps/dev-recruiters/src/components/modules/NewJoinPageComponent/RolesComponent/index.tsx b/apps/dev-recruiters/src/components/modules/NewJoinPageComponent/RolesComponent/index.tsx index a309d81a3..6118196d0 100644 --- a/apps/dev-recruiters/src/components/modules/NewJoinPageComponent/RolesComponent/index.tsx +++ b/apps/dev-recruiters/src/components/modules/NewJoinPageComponent/RolesComponent/index.tsx @@ -36,8 +36,6 @@ export default function RolesComponent({ fetchProjects, resetFilters, } = useProjectRole(); - - console.log('here', filteredProjects); useEffect(() => { if (projects && !projectsLoaded) { fetchProjects(projects); @@ -210,6 +208,7 @@ export default function RolesComponent({ )} diff --git a/apps/dev-recruiters/src/components/modules/NewJoinPageComponent/RolesList/index.tsx b/apps/dev-recruiters/src/components/modules/NewJoinPageComponent/RolesList/index.tsx index dae1dda5d..29761c4a4 100644 --- a/apps/dev-recruiters/src/components/modules/NewJoinPageComponent/RolesList/index.tsx +++ b/apps/dev-recruiters/src/components/modules/NewJoinPageComponent/RolesList/index.tsx @@ -5,12 +5,56 @@ import { List } from '../../../../components/modules/OpportunitiesAggregator/fil import { Opportunity } from '@devlaunchers/models/opportunity'; import CollapsibleContainer from '../SelectRole/CollapsibleContainer'; +export function separateRoles(arr: Opportunity[]) { + const separatedGroups: { [key: string]: Opportunity[] } = { + ProductLead: [], + UxDesigner: [], + UxResearcher: [], + InformationArchitect: [], + LeadDeveloper: [], + BackEndDeveloper: [], + FrontEndDeveloper: [], + QaLead: [], + QaTester: [], + VoulunteerCordinator: [], + SocialMediaManager: [], + }; + arr.forEach((role) => { + if (parseInt(role?.id) === 8 || parseInt(role?.id) === 4) { + separatedGroups['ProductLead'].push(role); + } else if (parseInt(role?.id) === 6) { + separatedGroups['UxDesigner'].push(role); + } else if (parseInt(role?.id) === 7 || parseInt(role?.id) === 5) { + separatedGroups['UxResearcher'].push(role); + } else if (parseInt(role?.id) === 15) { + separatedGroups['LeadDeveloper'].push(role); + } else if (parseInt(role?.id) === 2) { + separatedGroups['BackEndDeveloper'].push(role); + } else if ( + parseInt(role?.id) === 1 || + parseInt(role?.id) === 10 || + parseInt(role?.id) === 16 + ) { + separatedGroups['FrontEndDeveloper'].push(role); + } else if (parseInt(role?.id) === 3) { + separatedGroups['QaTester'].push(role); + } + }); + + return separatedGroups; +} + interface Props { projects?: ProjectLite[]; projectsLoaded: boolean; + opportunities: Opportunity[]; } -export default function RolesList({ projects, projectsLoaded }: Props) { +export default function RolesList({ + projects, + projectsLoaded, + opportunities, +}: Props) { if (!projectsLoaded) return
loading please wait
; const [openPositions, setOpenPositions] = useState<{ @@ -28,11 +72,14 @@ export default function RolesList({ projects, projectsLoaded }: Props) { VoulunteerCordinator: [], SocialMediaManager: [], }); - console.log(projects); // Extract all opportunities from the projects and flatten them into a single array - const allOpportunities = projects.flatMap((project) => project.opportunities); - - function separateRoles(arr: Opportunity[]) { + /*const allOpportunities = projects.flatMap( + (project) => project.attributes.opportunities + ); + */ + const allOpportunities = opportunities.flatMap((opp) => opp); + console.log(allOpportunities); + /*function separateRoles(arr: Opportunity[]) { const separatedGroups: { [key: string]: Opportunity[] } = { ProductLead: [], UxDesigner: [], @@ -47,35 +94,33 @@ export default function RolesList({ projects, projectsLoaded }: Props) { SocialMediaManager: [], }; arr.forEach((role) => { - console.log(typeof parseInt(role.id)); - if (parseInt(role.id) === 8 || parseInt(role.id) === 4) { + if (parseInt(role?.id) === 8 || parseInt(role?.id) === 4) { separatedGroups['ProductLead'].push(role); - } else if (parseInt(role.id) === 6) { + } else if (parseInt(role?.id) === 6) { separatedGroups['UxDesigner'].push(role); - } else if (parseInt(role.id) === 7 || parseInt(role.id) === 5) { + } else if (parseInt(role?.id) === 7 || parseInt(role?.id) === 5) { separatedGroups['UxResearcher'].push(role); - } else if (parseInt(role.id) === 15) { + } else if (parseInt(role?.id) === 15) { separatedGroups['LeadDeveloper'].push(role); - } else if (parseInt(role.id) === 2) { + } else if (parseInt(role?.id) === 2) { separatedGroups['BackEndDeveloper'].push(role); } else if ( - parseInt(role.id) === 1 || - parseInt(role.id) === 10 || - parseInt(role.id) === 16 + parseInt(role?.id) === 1 || + parseInt(role?.id) === 10 || + parseInt(role?.id) === 16 ) { separatedGroups['FrontEndDeveloper'].push(role); - } else if (parseInt(role.id) === 3) { + } else if (parseInt(role?.id) === 3) { separatedGroups['QaTester'].push(role); } }); return separatedGroups; - } + }*/ useEffect(() => { setOpenPositions(separateRoles(allOpportunities)); }, [projects]); - return ( diff --git a/apps/dev-recruiters/src/components/modules/NewJoinPageComponent/SelectRole/CollapsibleContainer/index.tsx b/apps/dev-recruiters/src/components/modules/NewJoinPageComponent/SelectRole/CollapsibleContainer/index.tsx index 3ca05e65d..83f03ac01 100644 --- a/apps/dev-recruiters/src/components/modules/NewJoinPageComponent/SelectRole/CollapsibleContainer/index.tsx +++ b/apps/dev-recruiters/src/components/modules/NewJoinPageComponent/SelectRole/CollapsibleContainer/index.tsx @@ -23,8 +23,6 @@ const CollapsibleContainer: React.FC = ({ function handleRoleClick(role) { const roleJsonString = JSON.stringify(role); - console.log('here', openPositions); - localStorage.setItem('selectedRole', roleJsonString); router.push('/join/filter'); diff --git a/apps/dev-recruiters/src/components/modules/NewJoinPageComponent/UseProjectRole/index.tsx b/apps/dev-recruiters/src/components/modules/NewJoinPageComponent/UseProjectRole/index.tsx index 7c80db0fc..01b6fb32f 100644 --- a/apps/dev-recruiters/src/components/modules/NewJoinPageComponent/UseProjectRole/index.tsx +++ b/apps/dev-recruiters/src/components/modules/NewJoinPageComponent/UseProjectRole/index.tsx @@ -8,6 +8,7 @@ export default function useProjectRole() { const [projects, setProjects] = useState([]); const [projectsLoaded, setProjectsLoaded] = useState(false); const [filteredProjects, setFilteredProjects] = useState(); + const [opportunities, setOpportunities] = useState([]); const [opportunitiesLoaded, setOpportunitiesLoaded] = useState(false); const [projectParams, setProjectParams] = useState({ @@ -17,7 +18,6 @@ export default function useProjectRole() { maxCommit: 0, searchTerm: '', }); - // Apply Filters const SetProjectParams = (value: ProjectParams) => { setFilteredProjects(FilterProjects(projects, value)); @@ -25,16 +25,19 @@ export default function useProjectRole() { //#region Fetching Operations const fetchProjects = (projectsList: Project[]) => { - if (projectsList.length > 0) { + if (projectsList?.length > 0) { const list = projectsList.map((item: Project) => ({ id: item.id, - slug: item.slug, - catchPhrase: item.catchPhrase, - title: item.title, - description: item.description, - commitmentLevel: item.commitmentLevel, - opportunities: item.opportunities, - isPlatform: item.isPlatform, + attributes: { + slug: item.attributes.slug, + catchPhrase: item.attributes.catchPhrase, + title: item.attributes.title, + description: item.attributes.description, + commitmentLevel: item.attributes.commitmentLevel, + opportunities: item.attributes.opportunities, + + //isPlatform: item.attributes.isPlatform, + }, })); setProjects(list); setFilteredProjects(list); @@ -42,8 +45,6 @@ export default function useProjectRole() { } }; - console.log(projects); - function fetchOpportunities(opportunities: Opportunity[]) { if (opportunities.length <= 0) return; setOpportunities(opportunities); @@ -135,18 +136,21 @@ export default function useProjectRole() { //#region Single Project Filtering function FilterBySearchTerm(project: ProjectLite, params: ProjectParams) { - if (params.searchTerm) { + /*if (params.searchTerm) { return ( - project.title.toLowerCase().includes(params.searchTerm.toLowerCase()) || - project.opportunities.some((o) => + project.attributes.title + .toLowerCase() + .includes(params.searchTerm.toLowerCase()) + || + project.attributes.opportunities.data.some((o) => o.skills.some((s) => s! .interest!.toLowerCase()! .includes(params!.searchTerm!.toLowerCase()) - ) + ) ) ); - } + } */ return true; } @@ -160,46 +164,49 @@ function FilterProjectOpportunities( params.opportunity && params.opportunity.length > 0; const filterByCommitment = params.maxCommit > 0; - return project.opportunities.some( + return project.attributes.opportunities.data.some( (op) => - (!filterByLevel || params!.level!.includes(op!.level)) && - (!filterByOpportunity || params!.opportunity!.includes(op!.title)) && - (!filterByCommitment || op!.commitmentHoursPerWeek <= params!.maxCommit) + (!filterByLevel || params!.level!.includes(op!.attributes.level)) && + (!filterByOpportunity || + params!.opportunity!.includes(op!.attributes.title)) && + (!filterByCommitment || + op!.attributes.commitmentHoursPerWeek <= params!.maxCommit) ); } function FilterByLevel(project: ProjectLite, params: ProjectParams) { if (params.level && params.level.length > 0) { - return project.opportunities.some((op) => - params!.level!.includes(op!.level) + return project.attributes.opportunities.data.some((op) => + params!.level!.includes(op!.attributes.level) ); } return true; } function FilterByOpportunities(project: ProjectLite, params: ProjectParams) { if (params.opportunity && params.opportunity.length > 0) { - return project.opportunities.some((op) => - params!.opportunity!.includes(op!.title) + return project.attributes.opportunities.data.some((op) => + params!.opportunity!.includes(op!.attributes.title) ); } return true; } function FilterByCommitment(project: ProjectLite, params: ProjectParams) { if (params.maxCommit > 0) { - return project.opportunities.some( - (op) => op.commitmentHoursPerWeek <= params.maxCommit + return project.attributes.opportunities.data.some( + (op) => op.attributes.commitmentHoursPerWeek <= params.maxCommit ); } return true; } function FilterByProjectType(project: ProjectLite, params: ProjectParams) { if (params.projectType && params.projectType.length > 0) { - const isPlatform = - params.projectType.includes('Platform') && project.isPlatform; + /* const isPlatform = + params.projectType.includes('Platform') && project.attributes.isPlatform; const isIndependent = - params.projectType.includes('Independent') && !project.isPlatform; - - return isPlatform || isIndependent; + params.projectType.includes('Independent') && + !project.attributes.isPlatform; + */ + return 'isPlatform' || 'isIndependent'; } return true; } @@ -208,7 +215,8 @@ function FilterProject(project: ProjectLite, params: ProjectParams) { return ( FilterProjectOpportunities(project, params) && FilterByProjectType(project, params) && - FilterBySearchTerm(project, params) + FilterBySearchTerm(project, params) && + FilterByCommitment(project, params) ); } //#endregion @@ -224,10 +232,10 @@ export function FilterProjects(projects: ProjectLite[], params: ProjectParams) { return projects .filter((project) => FilterProject(project, params)) .sort((a, b) => { - if (a.title < b.title) { + if (a.attributes.title < b.attributes.title) { return -1; } - if (a.title > b.title) { + if (a.attributes.title > b.attributes.title) { return 1; } return 0; diff --git a/apps/dev-recruiters/src/components/modules/OpportunitiesAggregator/LongCard/LongCard.tsx b/apps/dev-recruiters/src/components/modules/OpportunitiesAggregator/LongCard/LongCard.tsx index ff0098af9..078cfa44e 100644 --- a/apps/dev-recruiters/src/components/modules/OpportunitiesAggregator/LongCard/LongCard.tsx +++ b/apps/dev-recruiters/src/components/modules/OpportunitiesAggregator/LongCard/LongCard.tsx @@ -23,7 +23,7 @@ export default function LongCard({ description, details }: Props) { <> -

Description

+

Description

{isExpanded ? ( @@ -38,7 +38,7 @@ export default function LongCard({ description, details }: Props) { remarkPlugins={[remarkGfm]} rehypePlugins={[rehypeRaw]} > - {details.slice(0, details.length / 2)} + {!details && details?.slice(0, details?.length / 2)} )} diff --git a/apps/dev-recruiters/src/components/modules/OpportunitiesAggregator/OpportunitiesAggregator.tsx b/apps/dev-recruiters/src/components/modules/OpportunitiesAggregator/OpportunitiesAggregator.tsx index 5ffdf2232..dd556c43e 100644 --- a/apps/dev-recruiters/src/components/modules/OpportunitiesAggregator/OpportunitiesAggregator.tsx +++ b/apps/dev-recruiters/src/components/modules/OpportunitiesAggregator/OpportunitiesAggregator.tsx @@ -77,7 +77,7 @@ const OpportunitiesAggregator: React.FunctionComponent = ({ catchPhrase={project.catchPhrase} commitmentLevel={project.commitmentLevel} isPlatform={project.isPlatform} - opportunities={project.opportunities} + opportunities={project.opportunities.data} slug={project.slug} /> ))} diff --git a/apps/dev-recruiters/src/components/modules/OpportunitiesAggregator/ShortCard/ShortCard.tsx b/apps/dev-recruiters/src/components/modules/OpportunitiesAggregator/ShortCard/ShortCard.tsx index 531f32319..2f095c9f1 100644 --- a/apps/dev-recruiters/src/components/modules/OpportunitiesAggregator/ShortCard/ShortCard.tsx +++ b/apps/dev-recruiters/src/components/modules/OpportunitiesAggregator/ShortCard/ShortCard.tsx @@ -55,8 +55,8 @@ const ShortCard = ({ Position Available
    - {opp.map((position) => ( - + {opp.map(({attributes: position, id}) => ( + {position.title} ))} diff --git a/apps/dev-recruiters/src/components/modules/OpportunitiesAggregator/filtering/ProjectsList/ProjectsList.tsx b/apps/dev-recruiters/src/components/modules/OpportunitiesAggregator/filtering/ProjectsList/ProjectsList.tsx index f17e93ce5..3c72110b7 100644 --- a/apps/dev-recruiters/src/components/modules/OpportunitiesAggregator/filtering/ProjectsList/ProjectsList.tsx +++ b/apps/dev-recruiters/src/components/modules/OpportunitiesAggregator/filtering/ProjectsList/ProjectsList.tsx @@ -1,7 +1,7 @@ -import NoProjects from "../NoProjects"; -import { ProjectLite } from "@devlaunchers/models/project"; -import ProjectListItem from "../ProjectsListItem"; -import { List } from "./StyledProjectsList"; +import NoProjects from '../NoProjects'; +import { ProjectLite } from '@devlaunchers/models/project'; +import ProjectListItem from '../ProjectsListItem'; +import { List } from './StyledProjectsList'; import { useEffect } from 'react'; interface Props { @@ -11,12 +11,11 @@ interface Props { export default function ProjectsList({ projects, projectsLoaded }: Props) { if (!projectsLoaded) return
    loading please wait
    ; - + useEffect(() => { const allOpportunities = projects.flatMap( - (project) => project.opportunities + (project) => project.attributes.opportunities ); - console.log(allOpportunities); const resultadoContagem = contarElementosRepetidos(allOpportunities); console.log(resultadoContagem); @@ -58,7 +57,6 @@ export default function ProjectsList({ projects, projectsLoaded }: Props) { return contagemElementos; } - console.log(projects); return ( {projects && projects.length > 0 ? ( diff --git a/apps/dev-recruiters/src/components/modules/OpportunitiesAggregator/filtering/ProjectsListItem/ProjectListItem.tsx b/apps/dev-recruiters/src/components/modules/OpportunitiesAggregator/filtering/ProjectsListItem/ProjectListItem.tsx index 683a5365a..7a7b874c1 100644 --- a/apps/dev-recruiters/src/components/modules/OpportunitiesAggregator/filtering/ProjectsListItem/ProjectListItem.tsx +++ b/apps/dev-recruiters/src/components/modules/OpportunitiesAggregator/filtering/ProjectsListItem/ProjectListItem.tsx @@ -43,20 +43,22 @@ export default function ProjectListItem({ project }: Props) { Positions Available / Level
      {expanded - ? project.opportunities.map((opportunity) => ( + ? project?.opportunities?.data?.map((opportunity) => (
    • {opportunity.title} - {opportunity.level}
    • )) - : project.opportunities.slice(0, 3).map((opportunity) => ( -
    • - {opportunity.title} - - - {opportunity.level} -
    • - ))} + : project.opportunities.data + .slice(0, 3) + .map((opportunity) => ( +
    • + {opportunity.title} + - + {opportunity.level} +
    • + ))}
    diff --git a/apps/dev-recruiters/src/components/modules/OpportunitiesAggregator/filtering/useProject.tsx b/apps/dev-recruiters/src/components/modules/OpportunitiesAggregator/filtering/useProject.tsx index 1dc02229d..1511cc932 100644 --- a/apps/dev-recruiters/src/components/modules/OpportunitiesAggregator/filtering/useProject.tsx +++ b/apps/dev-recruiters/src/components/modules/OpportunitiesAggregator/filtering/useProject.tsx @@ -43,13 +43,15 @@ export default function useProjects() { if (projectsList.length > 0) { const list = projectsList.map((item: Project) => ({ id: item.id, - slug: item.slug, - catchPhrase: item.catchPhrase, - title: item.title, - description: item.description, - commitmentLevel: item.commitmentLevel, - opportunities: item.opportunities, - isPlatform: item.isPlatform, + attributes: { + slug: item.attributes.slug, + catchPhrase: item.attributes.catchPhrase, + title: item.attributes.title, + description: item.attributes.description, + commitmentLevel: item.attributes.commitmentLevel, + opportunities: item.attributes.opportunities, + //isPlatform: item.attributes.isPlatform, + }, })); setProjects(list); setFilteredProjects(list); @@ -57,8 +59,6 @@ export default function useProjects() { } }; - console.log(projects) - function fetchOpportunities(opportunities: Opportunity[]) { if (opportunities.length <= 0) return; setOpportunities(opportunities); @@ -163,8 +163,10 @@ export default function useProjects() { function FilterBySearchTerm(project: ProjectLite, params: ProjectParams) { if (params.searchTerm) { return ( - project.title.toLowerCase().includes(params.searchTerm.toLowerCase()) || - project.opportunities.some((o) => + project.attributes.title + .toLowerCase() + .includes(params.searchTerm.toLowerCase()) || + project.attributes.opportunities.data.some((o) => o.skills.some((s) => s! .interest!.toLowerCase()! @@ -186,44 +188,46 @@ function FilterProjectOpportunities( params.opportunity && params.opportunity.length > 0; const filterByCommitment = params.maxCommit > 0; - return project.opportunities.some( + return project.attributes.opportunities.data.some( (op) => - (!filterByLevel || params!.level!.includes(op!.level)) && - (!filterByOpportunity || params!.opportunity!.includes(op!.title)) && - (!filterByCommitment || op!.commitmentHoursPerWeek <= params!.maxCommit) + (!filterByLevel || params!.level!.includes(op!.attributes.level)) && + (!filterByOpportunity || + params!.opportunity!.includes(op!.attributes.title)) && + (!filterByCommitment || + op!.attributes.commitmentHoursPerWeek <= params!.maxCommit) ); } function FilterByLevel(project: ProjectLite, params: ProjectParams) { if (params.level && params.level.length > 0) { - return project.opportunities.some((op) => - params!.level!.includes(op!.level) + return project.attributes.opportunities.data.some((op) => + params!.level!.includes(op!.attributes.level) ); } return true; } function FilterByOpportunities(project: ProjectLite, params: ProjectParams) { if (params.opportunity && params.opportunity.length > 0) { - return project.opportunities.some((op) => - params!.opportunity!.includes(op!.title) + return project.attributes.opportunities.data.some((op) => + params!.opportunity!.includes(op!.attributes.title) ); } return true; } function FilterByCommitment(project: ProjectLite, params: ProjectParams) { if (params.maxCommit > 0) { - return project.opportunities.some( - (op) => op.commitmentHoursPerWeek <= params.maxCommit + return project.attributes.opportunities.data.some( + (op) => op.attributes.commitmentHoursPerWeek <= params.maxCommit ); } return true; } function FilterByProjectType(project: ProjectLite, params: ProjectParams) { if (params.projectType && params.projectType.length > 0) { - const isPlatform = - params.projectType.includes('Platform') && project.isPlatform; - const isIndependent = - params.projectType.includes('Independent') && !project.isPlatform; + const isPlatform = undefined; + //params.projectType.includes('Platform') && project.isPlatform; + const isIndependent = undefined; + //params.projectType.includes('Independent') && !project.isPlatform; return isPlatform || isIndependent; } @@ -250,10 +254,10 @@ export function FilterProjects(projects: ProjectLite[], params: ProjectParams) { return projects .filter((project) => FilterProject(project, params)) .sort((a, b) => { - if (a.title < b.title) { + if (a.attributes.title < b.attributes.title) { return -1; } - if (a.title > b.title) { + if (a.attributes.title > b.attributes.title) { return 1; } return 0; diff --git a/apps/dev-recruiters/src/components/modules/OpportunitiesAggregatorWithRoles/OpportunitiesAggregator.tsx b/apps/dev-recruiters/src/components/modules/OpportunitiesAggregatorWithRoles/OpportunitiesAggregator.tsx index a18f423aa..c615c00fd 100644 --- a/apps/dev-recruiters/src/components/modules/OpportunitiesAggregatorWithRoles/OpportunitiesAggregator.tsx +++ b/apps/dev-recruiters/src/components/modules/OpportunitiesAggregatorWithRoles/OpportunitiesAggregator.tsx @@ -67,7 +67,7 @@ const OpportunitiesAggregator: React.FunctionComponent = ({ catchPhrase={project.catchPhrase} commitmentLevel={project.commitmentLevel} isPlatform={project.isPlatform} - opportunities={project.opportunities} + opportunities={project.opportunities.data} slug={project.slug} /> ))} diff --git a/apps/dev-recruiters/src/components/modules/OpportunitiesAggregatorWithRoles/filtering/ProjectsListItem/ProjectListItem.tsx b/apps/dev-recruiters/src/components/modules/OpportunitiesAggregatorWithRoles/filtering/ProjectsListItem/ProjectListItem.tsx index d6e250d2b..203909567 100644 --- a/apps/dev-recruiters/src/components/modules/OpportunitiesAggregatorWithRoles/filtering/ProjectsListItem/ProjectListItem.tsx +++ b/apps/dev-recruiters/src/components/modules/OpportunitiesAggregatorWithRoles/filtering/ProjectsListItem/ProjectListItem.tsx @@ -41,7 +41,7 @@ export default function ProjectListItem({ project }: Props) { Positions Available / Level
      {expanded - ? project.opportunities.map((opportunity) => ( + ? project?.opportunities?.map((opportunity) => (
    • {opportunity.title} - diff --git a/apps/dev-recruiters/src/components/modules/OpportunitiesAggregatorWithRoles/filtering/useProject.tsx b/apps/dev-recruiters/src/components/modules/OpportunitiesAggregatorWithRoles/filtering/useProject.tsx index e58c8db32..9b14271fa 100644 --- a/apps/dev-recruiters/src/components/modules/OpportunitiesAggregatorWithRoles/filtering/useProject.tsx +++ b/apps/dev-recruiters/src/components/modules/OpportunitiesAggregatorWithRoles/filtering/useProject.tsx @@ -38,7 +38,7 @@ export default function useProjects() { })); // Kris hack: Duplicate product entries for each embedded opportunity list = list.reduce((accumulator, project, index) => { - accumulator = [...accumulator, ...project.opportunities.map((opportunity) => { + accumulator = [...accumulator, ...project?.opportunities?.map((opportunity) => { return {...project, opportunities:[opportunity]}; })]; return accumulator; diff --git a/apps/dev-recruiters/src/components/modules/TalcommunityPage/HeaderTalPage/index.tsx b/apps/dev-recruiters/src/components/modules/TalcommunityPage/HeaderTalPage/index.tsx index 315da6a27..a8f17010b 100644 --- a/apps/dev-recruiters/src/components/modules/TalcommunityPage/HeaderTalPage/index.tsx +++ b/apps/dev-recruiters/src/components/modules/TalcommunityPage/HeaderTalPage/index.tsx @@ -1,5 +1,5 @@ import { Wrap } from '../StyledTalcommunityPage'; - +import React from 'react'; import { HeaderContainer, HeaderText, diff --git a/apps/dev-recruiters/src/components/modules/TalcommunityPage/StyledTalcommunityPage.ts b/apps/dev-recruiters/src/components/modules/TalcommunityPage/StyledTalcommunityPage.ts index 5460c7132..515acef78 100644 --- a/apps/dev-recruiters/src/components/modules/TalcommunityPage/StyledTalcommunityPage.ts +++ b/apps/dev-recruiters/src/components/modules/TalcommunityPage/StyledTalcommunityPage.ts @@ -2,7 +2,7 @@ import styled from 'styled-components'; import RocketIll from '../../../images/TalcommPage/rocket-illustration.png'; export const Wrapper = styled.div` - background-color: ${({ theme }) => theme?.colors?.SilverSandT20}; + background-color: #f0edee; `; export const Wrap = styled.div` @@ -16,9 +16,8 @@ export const Wrap = styled.div` rgba(0, 0, 0, 0.6) 100% ), url(${RocketIll}); - background-size: cover; background-position: center; - background-size: contain; + background-size: cover; height: 455px; flex-direction: column; justify-content: center; @@ -28,6 +27,6 @@ export const Wrap = styled.div` /* max-width: 100%; */ padding-left: 196px; padding-right: 196px; - display: flex; overflow: hidden; + background-color: #f0edee; `; diff --git a/apps/dev-recruiters/src/components/modules/TalcommunityPage/TalFormPage/index.tsx b/apps/dev-recruiters/src/components/modules/TalcommunityPage/TalFormPage/index.tsx index 323179cdd..841ddc991 100644 --- a/apps/dev-recruiters/src/components/modules/TalcommunityPage/TalFormPage/index.tsx +++ b/apps/dev-recruiters/src/components/modules/TalcommunityPage/TalFormPage/index.tsx @@ -1 +1 @@ -export { default } from "./signUpForm"; +export { default } from './signUpForm'; diff --git a/apps/dev-recruiters/src/components/modules/TalcommunityPage/TalFormPage/signUpForm.tsx b/apps/dev-recruiters/src/components/modules/TalcommunityPage/TalFormPage/signUpForm.tsx index cb8e0b5fa..e6e06440e 100644 --- a/apps/dev-recruiters/src/components/modules/TalcommunityPage/TalFormPage/signUpForm.tsx +++ b/apps/dev-recruiters/src/components/modules/TalcommunityPage/TalFormPage/signUpForm.tsx @@ -2,15 +2,18 @@ import { atoms, organisms } from '@devlaunchers/components/src/components'; import theme from '@devlaunchers/components/src/styles/theme'; import FormErrorScroller from '@devlaunchers/components/src/utils/formErrorScroller'; import { Opportunity } from '@devlaunchers/models'; -// import { NewApplicant } from '@devlaunchers/models/newApplicant'; -import { Newtalcommuser } from '@devlaunchers/models/talcommuser'; +import { Checkbox } from '@devlaunchers/components/src/components/Checkbox'; + import { agent } from '@devlaunchers/utility'; import { Field, Form, Formik, FormikHelpers } from 'formik'; +import React from 'react'; import { useState } from 'react'; import { ThemeProvider } from 'styled-components'; import * as Yup from 'yup'; import ConfirmationModal from '../../DetailedPage/Confirmation/ConfirmationModal'; import { useRouter } from 'next/router'; +import BoxContainer from '../../../common/BoxContainer'; +import { Wrapper } from '../StyledTalcommunityPage'; // interface FormFields extends Omit { // level: NewApplicant['level'] | ''; @@ -46,141 +49,144 @@ export default function TalCommForm({ handleCloseModal, position }: Props) { }; return ( - - - ) => { - setSubmitting(true); - agent.Talcommuser.post({ - ...values, - //@ts-ignore - // level: values.level.toLowerCase(), - skills: values.skills - .toString() - .split(',') - .map((skill) => ({ skill: skill })), - roles: 'title' as string, // role: position.title as string, - // project: router.query.project as string, - // id: '5' as string, // id: position.id as string, - }) - .then((res) => { - handleOpenConfirmationModal(); - setSubmitting(false); + + + + ) => { + setSubmitting(true); + agent.Talcommuser.post({ + ...values, + //@ts-ignore + // level: values.level.toLowerCase(), + skills: values.skills + .toString() + .split(',') + .map((skill) => ({ skill: skill })), + roles: 'title' as string, // role: position.title as string, + // project: router.query.project as string, + // id: '5' as string, // id: position.id as string, }) - .catch((error) => { - setSubmitting(false); - console.log(error); - console.log(error.response); - console.log(error.response.data); - console.log(error.response.status); - }); - }} - validationSchema={SignupSchema} - > - {({ errors, setFieldValue, touched, values }) => ( - - + .then((res) => { + handleOpenConfirmationModal(); + setSubmitting(false); + }) + .catch((error) => { + setSubmitting(false); + console.log(error); + console.log(error.response); + console.log(error.response.data); + console.log(error.response.status); + }); + }} + validationSchema={SignupSchema} + > + {({ errors, setFieldValue, touched, values }) => ( + + - - - - - + + + + + - - + + - - We require users to be 18 years old or older. Please confirm - below. - - - - - - Submit - + + We require users to be 18 years old or older. Please + confirm below. + + + + + + SUBMIT + + - - - - - - )} - - + + + + + )} + + + ); } diff --git a/apps/dev-recruiters/src/components/modules/TalcommunityPage/TalFormPage/styledSignupForm.ts b/apps/dev-recruiters/src/components/modules/TalcommunityPage/TalFormPage/styledSignupForm.ts index a5e9dc059..23e6ccbf4 100644 --- a/apps/dev-recruiters/src/components/modules/TalcommunityPage/TalFormPage/styledSignupForm.ts +++ b/apps/dev-recruiters/src/components/modules/TalcommunityPage/TalFormPage/styledSignupForm.ts @@ -11,7 +11,7 @@ export const Label = styled.label` font-size: ${({ theme }) => theme.fontSizes.small}; `; export const Input = styled.input<{ height?: string; width?: string }>` - background-color: ${({ theme }) => theme.colors.SilverSand}; + background-color: #f0edee}; height: ${({ height }) => height || 'auto'}; box-shadow: 0px 3px 3px rgba(0, 0, 0, 0.25); border-radius: 30px; @@ -23,7 +23,7 @@ export const Input = styled.input<{ height?: string; width?: string }>` `; export const TextArea = styled.textarea.attrs({ wrap: 'hard' })` resize: none; - background-color: ${({ theme }) => theme.colors.SilverSand}; + background-color: #f0edee; height: 175.5px; box-shadow: 0px 3px 3px rgba(0, 0, 0, 0.25); border-radius: 30px; @@ -68,14 +68,14 @@ export const RadioWrapper = styled.div` export const CheckboxLabel = styled.label` font-size: ${({ theme }) => theme.fontSizes.small}; - background-color: ${({ theme }) => theme.colors.SilverSand}; + background-color: #f0edee; border-radius: 1.875rem; padding: 1.25rem; margin-bottom: 1.25rem; `; export const Slider = styled.input.attrs({ type: 'range' })` - background-color: ${({ theme }) => theme.colors.SilverSand}; + background-color: #f0edee; border-radius: 30px; padding: 0.5rem; width: 40%; @@ -85,7 +85,7 @@ export const Slider = styled.input.attrs({ type: 'range' })` export const FileUpload = styled.input.attrs({ type: 'file' })` margin-top: 1.25rem; color: ${({ theme }) => theme?.colors?.Black}; - background-color: ${({ theme }) => theme.colors.SilverSand}; + background-color: #f0edee; border-radius: 30px; width: 40%; `; diff --git a/apps/dev-recruiters/src/components/modules/TalcommunityPage/index.tsx b/apps/dev-recruiters/src/components/modules/TalcommunityPage/index.tsx index a3d9c8794..5acd9436e 100644 --- a/apps/dev-recruiters/src/components/modules/TalcommunityPage/index.tsx +++ b/apps/dev-recruiters/src/components/modules/TalcommunityPage/index.tsx @@ -7,7 +7,7 @@ import { HeaderTalPage } from './HeaderTalPage'; const TalcommunityPage = () => { return ( - + diff --git a/apps/dev-recruiters/src/components/modules/ThankyouPage/index.tsx b/apps/dev-recruiters/src/components/modules/ThankyouPage/index.tsx index ecf4d01dd..f6e888693 100644 --- a/apps/dev-recruiters/src/components/modules/ThankyouPage/index.tsx +++ b/apps/dev-recruiters/src/components/modules/ThankyouPage/index.tsx @@ -1,28 +1,38 @@ import React from 'react'; import { - BaseText, HeaderContainer, - HeaderText, SubHeaderContainer, - SubHeaderText, Wrapper, + BtnSignUp, } from './styles'; import BoxContainer from '../../common/BoxContainer'; -import { Wrap } from '../TalcommunityPage/StyledTalcommunityPage'; +import { useRouter } from 'next/router'; export function ThankyouPage() { + const router = useRouter(); + const routeChange = (e) => { + e.preventDefault(); + router.push('/join/filter'); + }; return ( <> - Thank you! - - Your application for the Dev Launchers Talent Community has been - received. + + Thank you! + + Your application for the Dev Launchers Talent Community has been + received. + We will email you when a volunteer role that matches your skillsets and/or interests opens up! - + + + VIEW CURRENT OPEN ROLES + + + ); diff --git a/apps/dev-recruiters/src/components/modules/ThankyouPage/styles.ts b/apps/dev-recruiters/src/components/modules/ThankyouPage/styles.ts index 2c9745373..2d91aeda1 100644 --- a/apps/dev-recruiters/src/components/modules/ThankyouPage/styles.ts +++ b/apps/dev-recruiters/src/components/modules/ThankyouPage/styles.ts @@ -18,6 +18,23 @@ export const HeaderContainer = styled.div` padding-bottom: 56px; `; +export const BtnSignUp = styled.button` + background-color: black; + border-radius: 10px; + color: white; + padding: 10px 40px; + font-size: 16px; + cursor: pointer; + font-family: 'Nunito Sans', sans-serif; + background-color: #295774; + text-decoration: none; + font-weight: 400; + line-height: 24px; + letter-spacing: 0.64px; + word-wrap: break-word; + justify-content: center; +`; + export const SubHeaderContainer = styled.div` display: flex; flex-direction: column; @@ -36,30 +53,7 @@ export const SubHeaderContainer = styled.div` padding-bottom: 0; `; -export const BaseText = styled.div` - word-wrap: break-word; - color: ${({ theme }) => theme?.colors?.NEUTRAL_1}; -`; - -export const HeaderText = styled(BaseText)` - font-size: 44px; - font-family: 'Abel', sans-serif; - font-weight: 400; - color: white; - overflow: hidden; - position: relative; -`; - -export const SubHeaderText = styled(BaseText)` - font-size: 20px; - font-family: 'Nunito Sans', sans-serif; - color: white; - font-weight: 400; - padding-bottom: 24px; - overflow: hidden; - position: relative; -`; export const Wrapper = styled.div` - background-color: ${({ theme }) => theme?.colors?.SilverSandT20}; - height: 500px; + background-color: #f0edee !important; + /* height: 500px; */ `; diff --git a/apps/dev-recruiters/src/contexts/SelectRoleContext.tsx b/apps/dev-recruiters/src/contexts/SelectRoleContext.tsx index 2d1ae348e..fe8bcc38e 100644 --- a/apps/dev-recruiters/src/contexts/SelectRoleContext.tsx +++ b/apps/dev-recruiters/src/contexts/SelectRoleContext.tsx @@ -42,7 +42,6 @@ export function OpportunitiesProvider({ commitmentRange, setCommitmentRange, }; - return ( {children} diff --git a/apps/dev-recruiters/src/contexts/UserDataContext.js b/apps/dev-recruiters/src/contexts/UserDataContext.js deleted file mode 100644 index 55cef2619..000000000 --- a/apps/dev-recruiters/src/contexts/UserDataContext.js +++ /dev/null @@ -1,62 +0,0 @@ -import React from "react"; -import constate from "constate"; // State Context Object Creator -import axios from "axios"; - - -const DEFAULT_USER = { - id: 0, - name: "", - username: "", - email: "", - bio: "", - profilePictureUrl: "", - socialMediaLinks: [], - totalPoints: 0, - totalSeasonPoints: 0, - availablePoints: 0, - volunteerHours: 0, - discord: { - id: 0, - avatar: "", - username: "", - discriminator: "", - }, -}; - -// Built from this article: https://www.sitepoint.com/replace-redux-react-hooks-context-api/ - -// Step 1: Create a custom hook that contains your state and actions -function useUserData() { - const [userData, setUserData] = React.useState(DEFAULT_USER); - - React.useEffect(() => { - // Setting timeout because of environment variable hack - axios(`${process.env.NEXT_PUBLIC_STRAPI_URL}/users/me`, { - withCredentials: true, - }) - .then(({ data: currentUser }) => { - setUserData({ - id: currentUser.id, - name: currentUser.profile.displayName, - username: currentUser.username, - email: currentUser.email, - bio: currentUser.profile.bio, - profilePictureUrl: currentUser.profile.profilePictureUrl, - socialMediaLinks: currentUser.profile.socialMediaLinks, - totalPoints: currentUser.point.totalPoints, - totalSeasonPoints: currentUser.point.totalSeasonPoints, - availablePoints: currentUser.point.availablePoints, - volunteerHours: currentUser.point.volunteerHours, - }); - }) - .catch(() => { - // setUserData({ id: "invalid" }); - }); - }, []); - - return { userData }; -} - -// Step 2: Declare your context state object to share the state with other components -const [UserDataProvider, useUserDataContext] = constate(useUserData); -export { UserDataProvider, useUserDataContext }; diff --git a/apps/dev-recruiters/src/custom.d.ts b/apps/dev-recruiters/src/custom.d.ts index 7d6d6655b..781dd3878 100644 --- a/apps/dev-recruiters/src/custom.d.ts +++ b/apps/dev-recruiters/src/custom.d.ts @@ -1,2 +1,3 @@ -declare module "*.png?webp"; -declare module "*.png"; +declare module '*.png?webp'; +declare module '*.png'; +declare module '*.jpg'; diff --git a/apps/dev-recruiters/src/images/Searchresults/starysky.jpg b/apps/dev-recruiters/src/images/Searchresults/starysky.jpg new file mode 100644 index 000000000..a740d7cd4 Binary files /dev/null and b/apps/dev-recruiters/src/images/Searchresults/starysky.jpg differ diff --git a/apps/dev-recruiters/src/images/Searchresults/starysky_old.jpg b/apps/dev-recruiters/src/images/Searchresults/starysky_old.jpg new file mode 100644 index 000000000..bd89f9f31 Binary files /dev/null and b/apps/dev-recruiters/src/images/Searchresults/starysky_old.jpg differ diff --git a/apps/dev-recruiters/src/pages/[slug]/apply.tsx b/apps/dev-recruiters/src/pages/[slug]/apply.tsx index 95ecead1e..b20d67b70 100644 --- a/apps/dev-recruiters/src/pages/[slug]/apply.tsx +++ b/apps/dev-recruiters/src/pages/[slug]/apply.tsx @@ -4,6 +4,7 @@ import BoxContainer from '../../components/common/BoxContainer'; import TitledHeader from '../../components/common/TitledHeader'; function appldy() { + console.log('appsdev-recruiterssrcpages[slug]apply.tsx'); return ( <> @@ -15,7 +16,10 @@ function appldy() { /> - + { - const res = await fetch( - `${process.env.NEXT_PUBLIC_STRAPI_URL}/projects?_publicationState=live` + const result = await agent.Projects.list( + new URLSearchParams('populate=*&publicationState=live') + ); + //const res = await fetch( + // `${process.env.NEXT_PUBLIC_STRAPI_URL}/projects?_publicationState=live` + //); + let projects = result?.filter( + (p) => p.attributes.opportunities?.data?.length > 0 ); - const result: Project[] = await res.json(); - const projects = result?.filter((p) => p.opportunities?.length > 0); - + projects = projects.map((projects) => projects.attributes); // Flatten strapiv4 response const projectsSlugs = projects.map((project) => ({ params: { slug: project.slug, }, })); - return projectsSlugs; }; @@ -30,48 +34,66 @@ export const getStaticPaths: GetStaticPaths = async () => { fallback: 'blocking', }; }; - export const getStaticProps: GetStaticProps = async ({ params }) => { - const projectsRes = await fetch( - `${process.env.NEXT_PUBLIC_STRAPI_URL}/projects/${params.slug}` + const { attributes: project }: Project = await agent.Projects.get( + params.slug as string, + new URLSearchParams(`populate=*&publicationState=live`) ); - const opportuntiesRes = await fetch( - `${process.env.NEXT_PUBLIC_STRAPI_URL}/opportunities?projects.slug=${params.slug}` + let opportunities = await agent.Opportunities.list( + new URLSearchParams( + `populate=*&filters[projects][slug][$eq]=${params.slug}` + ) ); + // Restructure data returned from the API to flatten and make resemble data returned from old API + // Any relational data set up in Strapi should be flattened here + // We could `create a reusable function to handle this more elegantly + // project = { + // ...project, + // team: { + // leaders: project.team ? project.team?.leaders?.map(leader => leader.leader?.data.attributes) : ``, + // members: project.team ? project.team?.members?.map(member => member.member?.data.attributes) : `` + // }, + // interests: project.interests?.data.map(interest => interest.attributes), + // opportunities: project.opportunities?.data.map(opportunity => opportunity.attributes) - const project: Project = await projectsRes.json(); + // }; - const commitments = project.opportunities.map( - (opp) => opp.commitmentHoursPerWeek + const commitments = project?.opportunities?.data?.map( + (opp) => opp.attributes.commitmentHoursPerWeek ); - const maxCommitment = Math.max(...commitments); - const minCommitment = Math.min(...commitments); - project.commitmentLevel = `${minCommitment} - ${maxCommitment}`; - - const opportunites: Opportunity[] = await opportuntiesRes.json(); + const maxCommitment = + commitments === undefined ? 0 : Math.max(...commitments); + const minCommitment = + commitments === undefined ? 0 : Math.min(...commitments); + //project.commitmentLevel = `${minCommitment} - ${maxCommitment}`; - return { - props: { - project: project, - opportunites: opportunites, - maxCommitment, - minCommitment, - }, - revalidate: 10, - }; + opportunities = opportunities.map((opportunity) => opportunity.attributes); + return project !== undefined + ? { + props: { + project: project, + opportunites: opportunities, + maxCommitment, + minCommitment, + }, + revalidate: 10, + } + : { notFound: true }; }; +interface Props { + project: Project; + opportunites: Opportunity[]; + maxCommitment: number; + minCommitment: number; +} + export default function DetailedPage({ project, opportunites, maxCommitment, minCommitment, -}: { - project: Project; - opportunites: Opportunity[]; - maxCommitment: number; - minCommitment: number; -}) { +}: Props) { return ( <> @@ -83,7 +105,10 @@ export default function DetailedPage({ > - + { router.push(router.asPath.replace('/#', '')); } }; - -function MyApp(props) { +export default function MyApp(props) { const router = useRouter(); hashRedirect(router); - return ( @@ -63,4 +61,4 @@ function MyApp(props) { ); } -export default MyApp; +//export default MyApp; diff --git a/apps/dev-recruiters/src/pages/_document.tsx b/apps/dev-recruiters/src/pages/_document.tsx index f7693605e..1eff61681 100644 --- a/apps/dev-recruiters/src/pages/_document.tsx +++ b/apps/dev-recruiters/src/pages/_document.tsx @@ -1,7 +1,7 @@ // eslint-disable-next-line @next/next/no-document-import-in-page -import Document, { Html, Head, Main, NextScript } from "next/document"; +import Document, { Html, Head, Main, NextScript } from 'next/document'; -import { ServerStyleSheet } from "styled-components"; +import { ServerStyleSheet } from 'styled-components'; export default class MyDocument extends Document { render() { diff --git a/apps/dev-recruiters/src/pages/confirmation.tsx b/apps/dev-recruiters/src/pages/confirmation.tsx index 4c8c38f98..4810d1fea 100644 --- a/apps/dev-recruiters/src/pages/confirmation.tsx +++ b/apps/dev-recruiters/src/pages/confirmation.tsx @@ -17,7 +17,7 @@ function confirmation() { { let projects: Project[] = []; let opportunities: Opportunity[] = []; try { const result = await agent.Projects.list( - new URLSearchParams('_publicationState=live') + new URLSearchParams('_publicationState=live&populate=opportunities') + ); + projects = result.filter( + (p: Project) => p.attributes?.opportunities?.data.length > 0 ); - projects = result.filter((p: Project) => p.opportunities.length > 0); projects.map((project) => { - const commitments = project.opportunities.map( - (opp) => opp.commitmentHoursPerWeek + const commitments = project.attributes.opportunities.data.map( + (opp) => opp.attributes.commitmentHoursPerWeek ); // console.log(commitments); const maxCommitment = Math.max(...commitments); const minCommitment = Math.min(...commitments); - project.commitmentLevel = `${minCommitment} - ${maxCommitment}`; + project.attributes.commitmentLevel = `${minCommitment} - ${maxCommitment}`; return project; }); } catch (error) { @@ -31,11 +32,12 @@ export const getStaticProps: GetStaticProps = async (context) => { try { const result = await agent.Opportunities.list(); - opportunities = result.filter((o: Opportunity) => o.projects.length > 0); + opportunities = result.filter( + (o: Opportunity) => o.attributes.projects.data.length > 0 + ); } catch (error) { console.error('An error occurred while fetching Opportunities', error); } - return { props: { projects, @@ -49,52 +51,57 @@ interface Props { projects: Project[]; opportunities: Opportunity[]; } -const FilterPage = ({ projects, opportunities }: Props) => ( - <> - - Open roles - - +const FilterPage = ({ projects, opportunities }: Props) => { + return ( + <> + + Open roles + + - - - - - + + + + + - - - - - - - + + + + + + + - - - - -); + + + + + ); +}; export default FilterPage; diff --git a/apps/dev-recruiters/src/pages/index.tsx b/apps/dev-recruiters/src/pages/index.tsx index da23ce0da..af215eeae 100644 --- a/apps/dev-recruiters/src/pages/index.tsx +++ b/apps/dev-recruiters/src/pages/index.tsx @@ -1,116 +1,124 @@ -import Head from 'next/head'; -import SignUpForm from '../components/modules/FormPage/signUpForm'; -import BoxContainer from '../components/common/BoxContainer'; -import { Wrapper } from '@components/modules/OpportunitiesAggregator/StyledOpportunitiesAggregator'; -import { ThemeProvider } from 'styled-components'; -import theme from '../styles/theme'; -import NewJoinPageComponent from '../components/modules/NewJoinPageComponent'; -import { GetStaticProps } from 'next'; -import { Project } from '@devlaunchers/models/project'; -import { Opportunity } from '@devlaunchers/models/opportunity'; -import agent from '@devlaunchers/utility/agent'; -import { useRouter } from 'next/router'; -import { OpportunitiesProvider } from '../contexts/SelectRoleContext'; - -export const getStaticProps: GetStaticProps = async (context) => { - let projects: Project[] = []; - let opportunities: Opportunity[] = []; - try { - const result = await agent.Projects.list( - new URLSearchParams('_publicationState=live') - ); - projects = result.filter((p: Project) => p.opportunities.length > 0); - projects.map((project) => { - const commitments = project.opportunities.map( - (opp) => opp.commitmentHoursPerWeek - ); - // console.log(commitments); - const maxCommitment = Math.max(...commitments); - const minCommitment = Math.min(...commitments); - project.commitmentLevel = `${minCommitment} - ${maxCommitment}`; - return project; - }); - } catch (error) { - console.error('An error occurred while fetching Projects', error); - } - - try { - const result = await agent.Opportunities.list(); - opportunities = result.filter((o: Opportunity) => o.projects.length > 0); - } catch (error) { - console.error('An error occurred while fetching Opportunities', error); - } - - return { - props: { - projects, - opportunities, - }, - revalidate: 10, - }; -}; - -interface Props { - projects: Project[]; - opportunities: Opportunity[]; -} - -const NewJoinPage = ({ projects, opportunities }: Props) => { - const router = useRouter(); - const { format } = router.query; - - return ( - <> - - Join - - - - - - - - - - - - - - - - - - - - - - - - ); -}; - -export default NewJoinPage; +import Head from 'next/head'; +import SignUpForm from '../components/modules/FormPage/signUpForm'; +import BoxContainer from '../components/common/BoxContainer'; +import { Wrapper } from '@components/modules/OpportunitiesAggregator/StyledOpportunitiesAggregator'; +import { ThemeProvider } from 'styled-components'; +import theme from '../styles/theme'; +import NewJoinPageComponent from '../components/modules/NewJoinPageComponent'; +import { GetStaticProps } from 'next'; +import { Project } from '@devlaunchers/models/project'; +import { Opportunity } from '@devlaunchers/models/opportunity'; +import agent from '@devlaunchers/utility/agent'; +import { useRouter } from 'next/router'; +import { OpportunitiesProvider } from '../contexts/SelectRoleContext'; + +export const getStaticProps: GetStaticProps = async (context) => { + let projects: Project[] = []; + let opportunities: Opportunity[] = []; + try { + const result = await agent.Projects.list( + new URLSearchParams({ + _publicationState: 'live', + populate: 'opportunities', + }) + ); + projects = result?.filter( + (p: Project) => p.attributes.opportunities.data.length > 0 + ); + try { + const result = await agent.Opportunities.list(); + opportunities = result.filter((o: Opportunity) => { + return o.attributes.projects.data.length > 0; + }); + } catch (error) { + console.error('An error occurred while fetching Opportunities', error); + } + + // opportunities = result?.attributes.opportunities.data.filter( + // (o: Opportunity) => console.log(o) + // ); + projects.map((project) => { + const commitments = project.attributes.opportunities.data.map( + (opp) => opp.attributes.commitmentHoursPerWeek + ); + const maxCommitment = Math.max(...commitments); + const minCommitment = Math.min(...commitments); + project.attributes.commitmentLevel = `${minCommitment} - ${maxCommitment}`; + return project; + }); + } catch (error) { + console.error('in src/[ages/index/tsx/getStaticProps'); + console.error('An error occurred while fetching Projects', error); + } + return { + props: { + projects, + opportunities, + }, + revalidate: 10, + }; +}; +interface Props { + projects: Project[]; + opportunities: Opportunity[]; +} + +const NewJoinPage = ({ projects, opportunities }: Props) => { + const router = useRouter(); + const { format } = router.query; + + return ( + <> + + Join + + + + + + + + + + + + + + + + + + + + + + + + ); +}; + +export default NewJoinPage; diff --git a/apps/dev-recruiters/src/pages/login.tsx b/apps/dev-recruiters/src/pages/login.tsx index ce5a54ea9..047e1b70c 100644 --- a/apps/dev-recruiters/src/pages/login.tsx +++ b/apps/dev-recruiters/src/pages/login.tsx @@ -15,7 +15,7 @@ function login() { /> - + ( <> - - + + + + ); diff --git a/apps/dev-recruiters/src/pages/second.tsx b/apps/dev-recruiters/src/pages/second.tsx index c7e51fab7..10676bbb6 100644 --- a/apps/dev-recruiters/src/pages/second.tsx +++ b/apps/dev-recruiters/src/pages/second.tsx @@ -1,13 +1,20 @@ import Head from 'next/head'; import ProductHeader from '../components/modules/DetailedPage/ProductHeader'; +import React from 'react'; import SignUpForm from '../components/modules/TalcommunityPage/TalFormPage/signUpForm'; import BoxContainer from '../components/common/BoxContainer'; import TalcommunityPage from '../components/modules/TalcommunityPage'; +import TalFormPage from '../components/modules/TalcommunityPage/TalFormPage'; +import { HeaderTalPage } from '../components/modules/TalcommunityPage/HeaderTalPage'; const SecondPage = () => ( <> - - + + {/* */} + + + {/* */} + ); diff --git a/apps/dev-recruiters/src/utils/EnvironmentVariables.js b/apps/dev-recruiters/src/utils/EnvironmentVariables.js deleted file mode 100644 index 314d8cba3..000000000 --- a/apps/dev-recruiters/src/utils/EnvironmentVariables.js +++ /dev/null @@ -1,30 +0,0 @@ -const PROD = { - envType: "PROD", - API_URL: "https://api.devlaunchers.org", - STRAPI_URL: "http://localhost:1337", - GOOGLE_AUTH_URL: "https://api.devlaunchers.org/connect/google", - DISCORD_AUTH_URL: - "https://discord.com/api/oauth2/authorize?client_id=709889509864636496&redirect_uri=https%3A%2F%2Fapi.devlaunchers.org%2Fusers%2Fauth%2Fdiscord%2Fcallback&response_type=code&scope=identify", -}; - -const STAGING = { - envType: "STAGING", - API_URL: "https://api-staging.devlaunchers.org", - STRAPI_URL: "https://api-staging.devlaunchers.org", - GOOGLE_AUTH_URL: "https://api-staging.devlaunchers.org/connect/google", - DISCORD_AUTH_URL: - "https://discord.com/api/oauth2/authorize?client_id=815294711983112194&redirect_uri=https%3A%2F%2Fapi-staging.devlaunchers.org%2Fusers%2Fauth%2Fdiscord%2Fcallback&response_type=code&scope=identify", -}; - -export const env = () => { - if (typeof window !== "undefined") { - const currentUrl = window.location.href; - if ( - currentUrl.indexOf("staging") !== -1 || - currentUrl.indexOf("localhost") !== -1 - ) { - return STAGING; - } - } - return PROD; -}; diff --git a/apps/dev-recruiters/src/utils/Logout.js b/apps/dev-recruiters/src/utils/Logout.js index d7247e97d..855b1929f 100644 --- a/apps/dev-recruiters/src/utils/Logout.js +++ b/apps/dev-recruiters/src/utils/Logout.js @@ -1,10 +1,9 @@ import Router from "next/router"; import axios from "axios"; -import { env } from "./EnvironmentVariables"; const Logout = () => { axios - .get(`${env().STRAPI_URL}/auth/logout`, { withCredentials: true }) + .get(`${process.env.NEXT_PUBLIC_STRAPI_URL}/auth/logout`, { withCredentials: true }) .then(() => { Router.reload(window.location.pathname); }); diff --git a/apps/gptbot/.babelrc b/apps/gptbot/.babelrc new file mode 100644 index 000000000..af1f9a5f5 --- /dev/null +++ b/apps/gptbot/.babelrc @@ -0,0 +1,4 @@ +{ + "presets": ["next/babel"], + "plugins": [["babel-plugin-styled-components", { "ssr": true }]] +} diff --git a/apps/gptbot/.eslintignore b/apps/gptbot/.eslintignore new file mode 100644 index 000000000..83b3fa336 --- /dev/null +++ b/apps/gptbot/.eslintignore @@ -0,0 +1,11 @@ +node_modules +production +staging +workload +.next +/out/ +.vscode +.github +*.json +*.lock +*.md diff --git a/apps/gptbot/.eslintrc-example.js b/apps/gptbot/.eslintrc-example.js new file mode 100644 index 000000000..2524b6c0e --- /dev/null +++ b/apps/gptbot/.eslintrc-example.js @@ -0,0 +1,38 @@ +// Workaround for https://github.com/eslint/eslint/issues/3458 (re-export of @rushstack/eslint-patch) +require("@devlaunchers/eslint-config-bases/patch/modern-module-resolution"); + +module.exports = { + // Be sure to set root to true in monorepo. + root: true, + // Will help typescript extended rules. + parserOptions: { + tsconfigRootDir: __dirname, + project: "tsconfig.json", + }, + ignorePatterns: ["**/node_modules", "**/.cache", "build", ".next"], + extends: [ + "@devlaunchers/eslint-config-bases/typescript", + "@devlaunchers/eslint-config-bases/sonar", + "@devlaunchers/eslint-config-bases/regexp", + "@devlaunchers/eslint-config-bases/react", + "@devlaunchers/eslint-config-bases/rtl", + + + + // Add specific rules for your framework if needed. + // ie: + // - nextjs: 'plugin:@next/next/core-web-vitals', + // - remix: '@remix-run/eslint-config', + // ... + + // Post configure the prettier base so there won't be + // any conficts between eslint / prettier + // "@devlaunchers/eslint-config-bases/prettier", + ], + rules: { + // Specific global rules for your app or package + }, + overrides: [ + // Specific file rules for your app or package + ], +}; \ No newline at end of file diff --git a/apps/gptbot/.gitignore b/apps/gptbot/.gitignore new file mode 100644 index 000000000..1437c53f7 --- /dev/null +++ b/apps/gptbot/.gitignore @@ -0,0 +1,34 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# local env files +.env.local +.env.development.local +.env.test.local +.env.production.local + +# vercel +.vercel diff --git a/apps/gptbot/.prettierignore b/apps/gptbot/.prettierignore new file mode 100644 index 000000000..f3144cdff --- /dev/null +++ b/apps/gptbot/.prettierignore @@ -0,0 +1,4 @@ +.next +/out/ +/node_modules/ +.github \ No newline at end of file diff --git a/apps/gptbot/README.md b/apps/gptbot/README.md new file mode 100644 index 000000000..38920c8dc --- /dev/null +++ b/apps/gptbot/README.md @@ -0,0 +1,36 @@ +This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). + +## Getting Started + +First, run the development server: + +```bash +npm install +npm run dev +# or +yarn install +yarn dev +``` + +Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. + +You can start editing the page by modifying `pages/index.js`. The page auto-updates as you edit the file. + +[API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/hello.js`. + +The `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages. + +## Learn More + +To learn more about Next.js, take a look at the following resources: + +- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. +- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. + +You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome! + +## Deploy on Vercel + +The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. + +Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details. diff --git a/apps/gptbot/next-env.d.ts b/apps/gptbot/next-env.d.ts new file mode 100644 index 000000000..3db3c4df2 --- /dev/null +++ b/apps/gptbot/next-env.d.ts @@ -0,0 +1,5 @@ +/// +/// + +// NOTE: This file should not be edited +// see https://nextjs.org/docs/basic-features/typescript for more information. diff --git a/apps/gptbot/next.config.js b/apps/gptbot/next.config.js new file mode 100644 index 000000000..151bec8e8 --- /dev/null +++ b/apps/gptbot/next.config.js @@ -0,0 +1,61 @@ +const withPlugins = require("next-compose-plugins"); +const imagesPlugin = require("next-optimized-images"); + + +/** + * @type {import('next').NextConfig} +* */ +const nextConfig = { + basePath: "/projects", + + async rewrites() { + return [ + { + source: "/:path*", + destination: `/:path*`, + }, + { + source: "/", + destination: "https://devlaunchers.org/", + basePath: false + }, + { + source: "/create", + destination: "https://devlaunchers.org/create", + basePath: false + }, + { + source: "/learn", + destination: `https://devlaunchers.org/learn`, + basePath: false + }, + { + source: "/support-us", + destination: `https://devlaunchers.org/support-us`, + basePath: false + }, + ]; + }, + + images: { + /* + next-images plugin is conflicting with Next.js 11 static import feature. + see the discussion here: + https://github.com/twopluszero/next-images/issues/73 + */ + domains: [ + "images.prismic.io", + "devlaunchersproduction.blob.core.windows.net", + ], + disableStaticImages: true, + }, + webpack5: true, + reactStrictMode: true, // It helps you avoid legacy code, and deprecated APIs. + eslint: { + // Warning: Dangerously allow production builds to successfully complete even if + // your project has ESLint errors. + // we have too many errors if you run npm run lint ,but after bug fixes we could enforce this. + ignoreDuringBuilds: true, + }, +}; +module.exports = withPlugins([[imagesPlugin], nextConfig]); diff --git a/apps/gptbot/package.json b/apps/gptbot/package.json new file mode 100644 index 000000000..4cc0b661e --- /dev/null +++ b/apps/gptbot/package.json @@ -0,0 +1,52 @@ +{ + "name": "@devlaunchers/gptbot", + "version": "0.1.0", + "private": true, + "dependencies": { + "@devlaunchers/components": "workspace:*", + "@devlaunchers/utility": "workspace:*", + "axios": "^0.27.2", + "next": "^12.2.3", + "next-optimized-images": "^2.6.2", + "react": "^17.0.2", + "react-dom": "^17.0.2" + }, + "devDependencies": { + "@devlaunchers/eslint-config-bases": "workspace:^", + "@types/react": "^17.0.0", + "@typescript-eslint/eslint-plugin": "^5.32.0", + "@typescript-eslint/parser": "^5.32.0", + "eslint": "8.21.0", + "eslint-config-airbnb": "19.0.4", + "eslint-config-airbnb-typescript": "^17.0.0", + "eslint-config-next": "^12.2.3", + "eslint-config-prettier": "^8.5.0", + "eslint-plugin-import": "^2.26.0", + "eslint-plugin-jsx-a11y": "^6.6.1", + "eslint-plugin-react": "^7.30.1", + "eslint-plugin-react-hooks": "^4.6.0", + "husky": "^8.0.1", + "lint-staged": "^13.0.3", + "prettier": "^2.7.1", + "semantic-release": "^19.0.3", + "typescript": "^4.7.4", + "webp-loader": "0.6.0", + "webpack": "^5.74.0" + }, + "scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start", + "prepare": "husky install", + "storybook": "start-storybook -p 6006 -s ./public", + "build-storybook": "build-storybook -s public", + "deploy-storybook": "storybook-to-ghpages", + "clean": "rimraf --no-glob ./tsconfig.tsbuildinfo", + "lint": "eslint . --ext .ts,.tsx,.js,.jsx,.cjs,.mjs,.mdx", + "fix-all-files": "eslint . --ext .ts,.tsx,.js,.jsx,.cjs,.mjs,.mdx --fix", + "typecheck": "tsc --project ./tsconfig.json --noEmit", + "test": "run-s 'test:*'", + "test:unit": "echo \"No tests yet\"", + "fix:staged-files": "lint-staged --allow-empty" + } +} diff --git a/apps/gptbot/public/favicon.ico b/apps/gptbot/public/favicon.ico new file mode 100644 index 000000000..4965832f2 Binary files /dev/null and b/apps/gptbot/public/favicon.ico differ diff --git a/apps/gptbot/public/vercel.svg b/apps/gptbot/public/vercel.svg new file mode 100644 index 000000000..fbf0e25a6 --- /dev/null +++ b/apps/gptbot/public/vercel.svg @@ -0,0 +1,4 @@ + + + \ No newline at end of file diff --git a/apps/gptbot/src/components/README.md b/apps/gptbot/src/components/README.md new file mode 100644 index 000000000..b650795ab --- /dev/null +++ b/apps/gptbot/src/components/README.md @@ -0,0 +1,7 @@ +Our components should be relatively aligned with the Smart vs Dumb aka Container component pattern seen here: https://medium.com/@thejasonfile/dumb-components-and-smart-components-e7b33a698d43 + +The 'modules' directory contains specialty components grouped by feature or domain. + +The 'common' directory contains components shared across routes, used in multiple areas of the app. + +We're using folder structures and conventions very similar to those described here: https://hackernoon.com/structuring-projects-and-naming-components-in-react-1261b6e18d76 diff --git a/apps/gptbot/src/content/README.md b/apps/gptbot/src/content/README.md new file mode 100644 index 000000000..ddc58a2f3 --- /dev/null +++ b/apps/gptbot/src/content/README.md @@ -0,0 +1,24 @@ +# This folder contains data fed into our content layer + +This layer has been created with the intent of moving towards a CMS tool like 'Contentful' in the future, or serving up that data from our backend (Firebase?). It allows us to manage all of the content displayed on the site in one location. + +[Currently, we're using some hacky Google Sheets fun to act as a backend] + +## File Naming Conventions + +You can think of each file in the 'collections' folder as a MongoDB collection. Filenames should therefore follow the MongoDB conventions here: https://arkusnexus.com/2016/09/12/coding-guidelines-mongodb/ + +- Key takeaways + - The name should be a plural of the types of documents to be saved. + - Use camelCase. Normally you shouldn’t have to because the collection name will be one word (plural of the types of documents saved). + +## JSON Conventions + +Google's JSON Style Guide: +https://google.github.io/styleguide/jsoncstyleguide.xml + +- Key takeaways + - Property names are camel cased + - Use double quotes + - Array types should have plural property names, all other property names should be singular + - Data should not be arbitrarily grouped for convenience diff --git a/apps/gptbot/src/custom.d.ts b/apps/gptbot/src/custom.d.ts new file mode 100644 index 000000000..d5c901ea0 --- /dev/null +++ b/apps/gptbot/src/custom.d.ts @@ -0,0 +1 @@ +declare module "*.png?webp"; diff --git a/apps/gptbot/src/pages/_app.jsx b/apps/gptbot/src/pages/_app.jsx new file mode 100644 index 000000000..f206c4c3a --- /dev/null +++ b/apps/gptbot/src/pages/_app.jsx @@ -0,0 +1,46 @@ +import { useRouter } from "next/router"; +import Script from "next/script"; +import { ThemeProvider } from "styled-components"; +import GlobalStyle from "../styles/globals"; +import oldTheme from "../styles/theme"; + +const hashRedirect = (router) => { + // Strip out hash from url (if any) so we can transition from HashRouter to BrowserRouter + if (router.asPath.startsWith("/#")) { + router.push(router.asPath.replace("/#", "")); + } +}; + +function MyApp(props) { + const router = useRouter(); + hashRedirect(router); + + return ( + <> + + +
      + +
      + {/* */} + {props.children} +
      +
      +
      + + ); +} + +export default MyApp; diff --git a/apps/gptbot/src/pages/_document.js b/apps/gptbot/src/pages/_document.js new file mode 100644 index 000000000..a33fb60a3 --- /dev/null +++ b/apps/gptbot/src/pages/_document.js @@ -0,0 +1,54 @@ +// eslint-disable-next-line @next/next/no-document-import-in-page +import Document, { Html, Head, Main, NextScript } from "next/document"; + +import { ServerStyleSheet } from "styled-components"; + +export default class MyDocument extends Document { + render() { + return ( + + + + + + + +
      + + + + ); + } + + static async getInitialProps(ctx) { + const sheet = new ServerStyleSheet(); + const originalRenderPage = ctx.renderPage; + + try { + ctx.renderPage = () => + originalRenderPage({ + enhanceApp: (App) => (props) => + sheet.collectStyles(), + }); + + const initialProps = await Document.getInitialProps(ctx); + return { + ...initialProps, + styles: ( + <> + {initialProps.styles} + {sheet.getStyleElement()} + + ), + }; + } finally { + sheet.seal(); + } + } +} diff --git a/apps/gptbot/src/pages/index.jsx b/apps/gptbot/src/pages/index.jsx new file mode 100644 index 000000000..3ad8dea0f --- /dev/null +++ b/apps/gptbot/src/pages/index.jsx @@ -0,0 +1,106 @@ +import Head from "next/head"; +import React, { useEffect } from 'react'; +import Button from "@devlaunchers/components/src/components/atoms/Button"; +import OpenResponse from "@devlaunchers/components/src/components/organisms/OpenResponse"; +import BaseLayer from "@devlaunchers/components/src/components/atoms/BaseLayer"; +import { send } from "../utils/chat.js"; +import { displayText } from "../utils/chat.js"; + +const ProjectsList = () => { + useEffect(() => { + const handleKeyPress = (event) => { + // Check if the pressed key is the "Enter" key (key code 13) + if (event.key === 'Enter') { + send(); // Call the send method + } + }; + + // Add an event listener for keydown when the component mounts + document.addEventListener('keydown', handleKeyPress); + + // Remove the event listener when the component unmounts to prevent memory leaks + return () => { + document.removeEventListener('keydown', handleKeyPress); + }; + }, []); // Empty dependency array means this effect runs once on mount + + return ( + <> + + Our Projects + + + + + + + + + + + + + + + + + +
      +
      + + +
      + + + + + ); +}; + +export default ProjectsList; \ No newline at end of file diff --git a/apps/gptbot/src/styled.d.ts b/apps/gptbot/src/styled.d.ts new file mode 100644 index 000000000..20c11929c --- /dev/null +++ b/apps/gptbot/src/styled.d.ts @@ -0,0 +1,9 @@ +// import original module declarations +import { Theme } from '@styles/theme'; +import {} from 'styled-components/cssprop' +import 'styled-components'; + +// and extend them! +declare module 'styled-components' { + export interface DefaultTheme extends Theme {} +} diff --git a/apps/ideaspace/src/styles/globals.js b/apps/gptbot/src/styles/globals.js similarity index 88% rename from apps/ideaspace/src/styles/globals.js rename to apps/gptbot/src/styles/globals.js index 4d1dbb687..3307c41d7 100644 --- a/apps/ideaspace/src/styles/globals.js +++ b/apps/gptbot/src/styles/globals.js @@ -3,7 +3,7 @@ import { normalize } from "styled-normalize"; const GlobalStyle = createGlobalStyle` ${normalize} - + html, body { padding: 0; @@ -31,19 +31,6 @@ body { width: 100%; height: 100%; font-size: 1.5rem; - - padding: 0; - margin: 0; - /*font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, - Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;*/ - font-family: "Nunito Sans", sans-serif; - font-weight: lighter; - /* color: ${({ theme }) => theme.colors.NEUTRAL_1}; */ - background-color:#f0edee; -} - -p { - margin-block: 0rem; } ::-webkit-scrollbar { @@ -70,13 +57,6 @@ p { text-align: left; } -.IdeasApp { - /* font-family: sans-serif; */ - text-align: center; - font-family: "Nunito Sans", sans-serif; - background-color:#f0edee; -} - a { width: auto; display: inline; @@ -107,7 +87,7 @@ h1 { // background-color:${({ theme }) => theme.colors.NEUTRAL_1}; // color:${({ theme }) => theme.colors.NEUTRAL_2}; - /* border-bottom: .3rem solid ${({ theme }) => theme.colors.NEUTRAL_1}; */ + border-bottom: .3rem solid ${({ theme }) => theme.colors.NEUTRAL_1}; } h2 { @@ -124,8 +104,8 @@ h4 { font-family: "Abel", sans-serif; } -h1, h2 { - font-family: "Abel", sans-serif; +strong { + font-weight: bold; } /* @@ -273,7 +253,7 @@ li { } h2 { - font-size: 1.2rem; + font-size: 2.2rem; } .collection--small-cards > .entry { diff --git a/apps/gptbot/src/styles/helpers.js b/apps/gptbot/src/styles/helpers.js new file mode 100644 index 000000000..4a3b9c571 --- /dev/null +++ b/apps/gptbot/src/styles/helpers.js @@ -0,0 +1,9 @@ +export const hexToRGBA = (hexColor, opacity = 1) => { + const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hexColor); + return result + ? `rgba(${parseInt(result[1], 16)},${parseInt(result[2], 16)},${parseInt( + result[3], + 16 + )},${opacity})` + : null; +}; diff --git a/apps/gptbot/src/styles/theme.ts b/apps/gptbot/src/styles/theme.ts new file mode 100644 index 000000000..62c7dfbbd --- /dev/null +++ b/apps/gptbot/src/styles/theme.ts @@ -0,0 +1,63 @@ +const theme = { + colors: { + NEUTRAL_1: "#1c1c1c", // dark + ACCENT_1: "#ff7f0e", // orange + ACCENT_2: "#ffab00", // yellow + ACCENT_3: "#3a7ca5", // dark-blue + ACCENT_4: "#81c3d7", // loght-blue + ACCENT_5: "#ffc30f", // orange-yellow + ACCENT_6: "#C5B1A0", // beige + NEUTRAL_2: "#f0edee", // light + + NEUTRAL_3:"#59687B", + NEUTRAL_4:"#C4C4C4", + NEUTRAL_5:"#89969F", + NEUTRAL_6:"#C3C0C0", + // Dev Recruiters theme colors + White: '#FFFFFF', + Black: '#000000', + BlackT60: 'rgba(0,0,0,0.6)', + BlackT38: 'rgba(0,0,0,0.38)', + + AntiFlashWhite: '#F1F4F5', + AntiFlashWhiteT40: 'rgba(241,244,245,0.4)', + + SilverSand: '#C4C4C4', + SilverSandT20: 'rgba(196,196,196,0.2)', + + LightGray: '#D3D4D6', + LightGrayT90: 'rgba(211, 212, 214, 0.9)', + + Cultured: '#F8F8F8', + CulturedT87: 'rgba(248, 248, 248, 0.87)', + + Red: '#FF0000', + RedT31: 'rgba(255,0,0,0.31)', + + AppleT48: 'rgba(102, 176, 75, 0.48)', + + OuterSpace: '#454D58', + DarkElectricBlue: '#59687B', + Crayola: '#30363E', + Gray: '#7E8288', + CoolGrey: '#89969F', + Argent: '#C3C0C0', + BlackCoral: '#5B6068', + Platinum: '#E5E5E5', + SonicSilver: '#6F747C', + BrightGray: '#EDEDED', + DavysGrey: '#4F5154', + }, + fonts: { + headline: 'Abel', + normal: 'Nunito Sans', + }, + fontSizes: { + small: '1em', + medium: '2em', + large: '3em', + }, +} as const; +type Theme = typeof theme; +export type { Theme }; +export default theme; \ No newline at end of file diff --git a/apps/gptbot/src/utils/GoogleAnalytics.js b/apps/gptbot/src/utils/GoogleAnalytics.js new file mode 100644 index 000000000..438d7bf53 --- /dev/null +++ b/apps/gptbot/src/utils/GoogleAnalytics.js @@ -0,0 +1,10 @@ +import ReactGA from "react-ga"; + +export const initGA = () => { + ReactGA.initialize("UA-180113136-1"); +}; + +export const logPageView = () => { + ReactGA.set({ page: window.location.pathname }); + ReactGA.pageview(window.location.pathname); +}; diff --git a/apps/gptbot/src/utils/Logout.js b/apps/gptbot/src/utils/Logout.js new file mode 100644 index 000000000..855b1929f --- /dev/null +++ b/apps/gptbot/src/utils/Logout.js @@ -0,0 +1,12 @@ +import Router from "next/router"; +import axios from "axios"; + +const Logout = () => { + axios + .get(`${process.env.NEXT_PUBLIC_STRAPI_URL}/auth/logout`, { withCredentials: true }) + .then(() => { + Router.reload(window.location.pathname); + }); +}; + +export default Logout; diff --git a/apps/gptbot/src/utils/ScrollToTop.js b/apps/gptbot/src/utils/ScrollToTop.js new file mode 100644 index 000000000..bbf080b01 --- /dev/null +++ b/apps/gptbot/src/utils/ScrollToTop.js @@ -0,0 +1,16 @@ +import { useEffect } from "react"; + +function ScrollToTop({ history }) { + useEffect(() => { + const unlisten = history.listen(() => { + window.scrollTo(0, 0); + }); + return () => { + unlisten(); + }; + }, [history]); + + return null; +} + +export default ScrollToTop; diff --git a/apps/gptbot/src/utils/TimeZoneConverter.js b/apps/gptbot/src/utils/TimeZoneConverter.js new file mode 100644 index 000000000..eaec2431f --- /dev/null +++ b/apps/gptbot/src/utils/TimeZoneConverter.js @@ -0,0 +1,29 @@ +import { DateTime } from "luxon"; + +export const ConvertCentralTime = (weekday = 6, hour, minute = 0) => + DateTime.fromObject({ + weekday, + hour, + minute, + zone: "UTC-5", + }).setZone("local"); + +export const ConvertCentralTimeString = ( + weekday = 0, + hour = 0, + minute = 0, + repeat = { weeks: 1 } +) => { + let centralDate = ConvertCentralTime(weekday, hour, minute); + if (DateTime.now() > centralDate) { + centralDate = centralDate.plus(repeat); + } + const localDate = centralDate.toLocaleString({ + weekday: "long", + month: "long", + day: "numeric", + hour: "numeric", + }); + const localTz = centralDate.toFormat("ZZZZ"); + return `${localDate} ${localTz}`; +}; diff --git a/apps/gptbot/src/utils/ValidateEmail.js b/apps/gptbot/src/utils/ValidateEmail.js new file mode 100644 index 000000000..507c75f65 --- /dev/null +++ b/apps/gptbot/src/utils/ValidateEmail.js @@ -0,0 +1,5 @@ +export default function validateEmail(email) { + const re = + /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; + return re.test(String(email).toLowerCase()); +} diff --git a/apps/gptbot/src/utils/chat.js b/apps/gptbot/src/utils/chat.js new file mode 100644 index 000000000..bf9d1df4d --- /dev/null +++ b/apps/gptbot/src/utils/chat.js @@ -0,0 +1,67 @@ +let questionNumber = 0; + +export async function send() { + let d = document.getElementById('chatbox'); + var i = d.value; + d.value = ''; + questionNumber++; + displayText('User: ' + i + ' : {Question #' + questionNumber + '}'); + await response(i); +} + +function response(i) { + let d = document.getElementById('chatbox'); + let b = document.getElementById('sendButton'); + //d.disabled = true; + + // Define the URL of the server where your Flask app is running + //const serverUrl = "http://127.0.0.1:5000/question"; + + // Create an object with the data you want to send + //const data = new URLSearchParams(); + //data.append("string", i); + + // Define the configuration for the POST request + /* + const requestOptions = { + method: "POST", + body: data, + headers: { + "Content-Type": "application/x-www-form-urlencoded", + }, + }; + */ + // Send the POST request + /* + fetch(serverUrl, requestOptions) + .then((response) => { + if (response.ok) { + return response.text(); + } else { + throw new Error(`Failed to make the request: ${response.status}`); + } + }) + .then((responseText) => { + responseNumber++; + // Handle the response here (e.g., printing it to the console) + displayText("Onboarding bot: " + responseText + "{Response #" + responseNumber + "}"); + d.disabled = false; + }) + .catch((error) => { + // Handle any errors that occurred during the request + console.error(error); + d.disabled = false; + + }); + */ + displayText('System: {Message #' + questionNumber + '} was ' + i); +} + +export function displayText(i) { + let d = document.getElementById('chat'); + d.innerHTML += msg(i); +} + +function msg(i) { + return `
      ${i}
      `; +} diff --git a/apps/gptbot/tsconfig.json b/apps/gptbot/tsconfig.json new file mode 100644 index 000000000..60a295680 --- /dev/null +++ b/apps/gptbot/tsconfig.json @@ -0,0 +1,32 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "baseUrl": "./src", + "target": "esnext", + "lib": ["dom", "dom.iterable", "esnext"], + "module": "esnext", + "jsx": "preserve", + "incremental": true, + "paths": { + "@/lib/*": ["./lib/*"], + "@/components/*": ["./components/*"], + "@/pages/*": ["./pages/*"], + "@/public/*": ["../public/*"], + "@/themes/*": ["./themes/*"], + "@devlaunchers/components/*": ["../../../packages/components/src/*"], + "@devlaunchers/components": ["../../../packages/components/src/index"], + "@devlaunchers/core-lib/*": ["../../../packages/core-lib/src/*"], + "@devlaunchers/core-lib": ["../../../packages/core-lib/src/index"] + } + }, + "exclude": ["**/node_modules", "**/.*/", ".next"], + "include": [ + "next-env.d.ts", + "**/*.ts", + "**/*.tsx", + "**/*.js", + "**/*.jsx", + "**/*.mjs", + "**/*.json" +, ".eslintrc-example.js" ] +} \ No newline at end of file diff --git a/apps/ideaspace/package.json b/apps/ideaspace/package.json index e7776f159..fa5b4c3b2 100644 --- a/apps/ideaspace/package.json +++ b/apps/ideaspace/package.json @@ -50,7 +50,6 @@ "eslint-config-next": "^12.2.3", "eslint-config-prettier": "^8.5.0", "husky": "^8.0.1", - "lint-staged": "^13.0.3", "postcss": "^8.4.14", "prettier": "^2.7.1", "typescript": "^4.7.4", diff --git a/apps/ideaspace/src/components/common/Button/Button.js b/apps/ideaspace/src/components/common/Button/Button.js index d050e6f2b..1666320f3 100644 --- a/apps/ideaspace/src/components/common/Button/Button.js +++ b/apps/ideaspace/src/components/common/Button/Button.js @@ -42,15 +42,15 @@ const onHoverBackgroundColor = (hexColor) => { }; const Button = styled.a` font-family: ${({ theme }) => theme.fonts.headline}, sans-serif; - background-color: ${({ theme, bgColor }) => bgColor || theme.colors.ACCENT_1}; - color: ${({ theme, textColor }) => textColor || theme.colors.NEUTRAL_2}; + background-color: ${({ theme, bgColor }) => bgColor || theme.colors.ORANGE}; + color: ${({ theme, textColor }) => textColor || theme.colors.GREYSCALE_OFF_WHITE}; transition: background-color 0.5s, color 0.5s; width: ${({ width }) => width || ""}; border: 0px; border-bottom: 3px solid ${({ theme, bgColor }) => - colorUnderButtons(bgColor || theme.colors.ACCENT_1)}; + colorUnderButtons(bgColor || theme.colors.ORANGE)}; cursor: pointer; font-size: ${({ fontSize }) => fontSize || "1.5rem"}; margin-top: ${({ marginTop }) => marginTop || ""}; @@ -98,9 +98,9 @@ const Button = styled.a` `} &:hover { background-color: ${({ theme, bgColor }) => - onHoverBackgroundColor(bgColor || theme.colors.ACCENT_2)}; + onHoverBackgroundColor(bgColor || theme.colors.YELLOW)}; color: ${({ theme, bgColor }) => - (bgColor && shadeColor(bgColor, -40)) || theme.colors.ACCENT_3}; + (bgColor && shadeColor(bgColor, -40)) || theme.colors.BLUE}; } `; export default Button; diff --git a/apps/ideaspace/src/components/common/CommonStyles.js b/apps/ideaspace/src/components/common/CommonStyles.js new file mode 100644 index 000000000..0eecc1de2 --- /dev/null +++ b/apps/ideaspace/src/components/common/CommonStyles.js @@ -0,0 +1,26 @@ +import styled from "styled-components"; + +export const HeadWapper = styled.div` + background-color: #FFFFFF; + padding: 6rem 1rem 4rem 1rem; + text-align: center; + + @media (max-width: 1712px) { + padding: 3rem 1rem 2rem 1rem; + } + + @media (max-width: 529px) { + padding: 5rem 1rem 1rem 1rem; + } +`; + +export const Headline = styled.div` + font-family: 'Abel'; + font-style: normal; + font-weight: 400; + font-size: 4rem; + line-height: 68px; + text-align: center; + letter-spacing: -0.02em; + color: #1C1C1C; +`; \ No newline at end of file diff --git a/apps/ideaspace/src/components/common/DialogBox/DialogBox.js b/apps/ideaspace/src/components/common/DialogBox/DialogBox.js index 004f9b039..f4c7d9c96 100644 --- a/apps/ideaspace/src/components/common/DialogBox/DialogBox.js +++ b/apps/ideaspace/src/components/common/DialogBox/DialogBox.js @@ -6,8 +6,10 @@ import { } from '@mui/material'; import IconButton from '@mui/material/IconButton'; import { atoms } from '@devlaunchers/components/src/components'; +import { Icons } from '@devlaunchers/components/src/assets'; +import DialogBoxButton from './DialogBoxButton'; -const useConfirm = (title, message) => { +const useConfirm = (title, message, buttonInfo) => { const [promise, setPromise] = useState(null); const confirm = () => new Promise((resolve, reject) => { @@ -32,6 +34,7 @@ const useConfirm = (title, message) => { { - {title} + + {title[1] == '' ? null : (title[1] == 'Success' ? : )} + {title[0]} + @@ -83,20 +89,11 @@ const useConfirm = (title, message) => { fontSize: '0.8rem' }} > - - CANCEL - - - LEAVE - + diff --git a/apps/ideaspace/src/components/common/DialogBox/DialogBoxButton.js b/apps/ideaspace/src/components/common/DialogBox/DialogBoxButton.js new file mode 100644 index 000000000..79833e476 --- /dev/null +++ b/apps/ideaspace/src/components/common/DialogBox/DialogBoxButton.js @@ -0,0 +1,58 @@ +import React from 'react'; +import { atoms } from '@devlaunchers/components/src/components'; + +const DialogBoxButton = ({ handleConfirmButton, handleCancelButton, buttonDetail }) => { + if (buttonDetail[0] == 'alternative primary') { + return ( + <> + + {buttonDetail[1]} + + + {buttonDetail[2]} + + + ) + } else if (buttonDetail[0] == 'primary alternative') { + return ( + <> + + {buttonDetail[1]} + + + {buttonDetail[2]} + + + ) + } else if (buttonDetail[0] == 'primary') { + return ( + <> + + {buttonDetail[1]} + + + ) + } +} + +export default DialogBoxButton; \ No newline at end of file diff --git a/apps/ideaspace/src/components/common/DialogBox/NoticeBox.js b/apps/ideaspace/src/components/common/DialogBox/NoticeBox.js deleted file mode 100644 index 231ca5fde..000000000 --- a/apps/ideaspace/src/components/common/DialogBox/NoticeBox.js +++ /dev/null @@ -1,95 +0,0 @@ -import Modal from 'react-modal'; -import { useState } from 'react'; -import { - Dialog, DialogActions, - DialogContent, DialogContentText, DialogTitle, -} from '@mui/material'; -import IconButton from '@mui/material/IconButton'; -import { atoms } from '@devlaunchers/components/src/components'; - -const getNotice= (title, message) => { - const [promise, setPromise] = useState(null); - - const confirm = () => new Promise((resolve, reject) => { - setPromise({ resolve }); - }); - - const handleClose = () => { - setPromise(null); - }; - - const handleCancel = () => { - promise?.resolve(true); - handleClose(); - }; - - const ConfirmationDialog = () => ( - - - - x - - - - {title} - - - - - {message} - - - - - - got it - - - - - ); - return [ConfirmationDialog, confirm]; -}; - -export default getNotice; \ No newline at end of file diff --git a/apps/ideaspace/src/components/common/Dropdown/DropdownButton.js b/apps/ideaspace/src/components/common/Dropdown/DropdownButton.js deleted file mode 100644 index 14ddc0a73..000000000 --- a/apps/ideaspace/src/components/common/Dropdown/DropdownButton.js +++ /dev/null @@ -1,42 +0,0 @@ -import React, { useEffect, useState, useRef } from "react"; -import { Wrapper, Toggle, Rooms } from "./StyledDropdownButton"; - -const DropdownButton = ({ toggleBtnText, dropdownItems, className }) => { - const [menuOpen, setMenuOpen] = useState(false); - const node = useRef(); - - const handleClickOutside = (e) => { - if (node.current.contains(e.target)) return; - // outside click - setMenuOpen(false); - }; - - useEffect(() => { - if (menuOpen) { - document.addEventListener("mousedown", handleClickOutside); - } else { - document.removeEventListener("mousedown", handleClickOutside); - } - - return () => { - document.removeEventListener("mousedown", handleClickOutside); - }; - }, [menuOpen]); - - return ( - - setMenuOpen(!menuOpen)} - as="button" - fontSize="1.2rem" - > - {toggleBtnText} - - - <>{dropdownItems} - - - ); -}; - -export default DropdownButton; diff --git a/apps/ideaspace/src/components/common/Dropdown/StyledDropdownButton.js b/apps/ideaspace/src/components/common/Dropdown/StyledDropdownButton.js deleted file mode 100644 index cc34302ff..000000000 --- a/apps/ideaspace/src/components/common/Dropdown/StyledDropdownButton.js +++ /dev/null @@ -1,38 +0,0 @@ -import styled from "styled-components"; -import Button from "../Button"; - -export const Wrapper = styled.div` - position: relative; - display: inline-block; -`; -export const Toggle = styled(Button)` - padding-left: 2rem; - padding-right: 2rem; -`; -export const Rooms = styled.div` - display: ${({ isOpen }) => (isOpen ? "block" : "none")}; - position: absolute; - right: 50%; - left: -50%; - background-color: #f1f1f1; - min-width: 160px; - overflow: auto; - box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.2); - border: 1px solid black; - z-index: 1; - - & > * { - color: black; - border: 1px solid black; - padding: 12px 16px; - text-decoration: none; - text-align: center; - display: block; - - &:hover, - &:focus { - background-color: #ddd; - color: #000; - } - } -`; diff --git a/apps/ideaspace/src/components/common/Dropdown/index.js b/apps/ideaspace/src/components/common/Dropdown/index.js deleted file mode 100644 index d06e95af1..000000000 --- a/apps/ideaspace/src/components/common/Dropdown/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from "./DropdownButton"; diff --git a/apps/ideaspace/src/components/common/IdeaCard/IdeaCard.js b/apps/ideaspace/src/components/common/IdeaCard/IdeaCard.js index 42e0f9674..90b2cc2be 100644 --- a/apps/ideaspace/src/components/common/IdeaCard/IdeaCard.js +++ b/apps/ideaspace/src/components/common/IdeaCard/IdeaCard.js @@ -1,154 +1,136 @@ -import React, { useState } from 'react'; +import { useState, useEffect } from 'react'; import Link from 'next/link'; import { atoms } from '@devlaunchers/components/src/components'; -import theme from '@devlaunchers/components/styles/theme'; -import { - ImgButton, - StatuBox, -} from './StyledIdeaCard'; -import commentSvg from '../../../images/comment.svg'; -import timeSvg from '../../../images/time.svg'; +import IdeaCardImg from './IdeaCardImg'; +import IdeaCardTag from './IdeaCardTag'; +import IdeaCardComment from './IdeaCardComment'; +import IdeaCardUpdated from './IdeaCardUpdated'; +import useConfirm from '../DialogBox/DialogBox'; +import { LikeButton } from '@devlaunchers/components/src/components/molecules'; +import { useUserDataContext } from '@devlaunchers/components/context/UserDataContext'; +import { agent } from '@devlaunchers/utility'; + function IdeaCard({ cards, cardType }) { - const [tag, setTag] = useState(''); - const [tagColor, setTagColor] = useState(''); - const [updated, setUpdated] = useState(''); - React.useEffect(() => { - if (cards.statu == "submitted"){ - setTag("submitted"); - setTagColor(theme.colors.LIGHT_BLUE_100); - } else if (cards.statu == "applying"){ - setTag("applying"); - setTagColor(theme.colors.YELLOW_100); - } else if (cards.statu == "approved"){ - setTag("approved"); - setTagColor(theme.colors.SUCCESS_100); - } else if (cards.statu == "archived"){ - setTag("archived"); - setTagColor(theme.colors.BLUE_100); + const [tagContent, setTagContent] = useState(cards.status); + const [buttonContent, setButtonContent] = useState(''); + const [urlPath, setUrlPath] = useState(''); + const [liked, setLiked] = useState(false); + const { isAuthenticated, userData } = useUserDataContext(); + + + const [UpdateFailure, confirmFailure] = useConfirm( + ['Unable to reactivate your idea', '', ''], + 'Please try again.', + ['primary', 'close'] + ); + + useEffect(() => { + if (cardType == "mine") { + if (tagContent !== "archived") { + setButtonContent(`WORKSHOP THIS IDEA`); + } else { + setButtonContent(`REACTIVATE THIS IDEA`); + } + setUrlPath(`/ideaspace/edit/${cards.id}`); } else { - setTag("workshopping"); - setTagColor(theme.colors.ORANGE_100); + setButtonContent(`HELP THIS IDEA`); + setUrlPath(`/ideaspace/workshop/${cards.id}`); } + }, [tagContent]); - const toBeOneSecond = 1000; - const secondsInDay = 86400; - const secondsInMonth = secondsInDay * 30; - const secondsInYear = secondsInMonth * 12; - const secondsInHour = 3600; - const secondsInMinute = 60; + const reactivateIdea = async () => { + cards['status'] = 'workshopping'; + setButtonContent(`WAIT`); - const difftime = (new Date() - new Date(cards.updated_at)) / toBeOneSecond; + try { + const res = await agent.Ideas.getIdea(cards.id, new URLSearchParams(`populate=*`)); - if (difftime > secondsInYear) { - const years = parseInt(difftime / secondsInYear); - if (years > 1) { - setUpdated(years + ' years ago'); - } else { - setUpdated(years + ' year ago'); - } - } else if (difftime > secondsInMonth) { - const months = parseInt(difftime / secondsInMonth); - if (months > 1) { - setUpdated(months + ' months ago'); - } else { - setUpdated(months + ' month ago'); - } - } else if (difftime > secondsInDay) { - const days = parseInt(difftime / secondsInDay); - if (days > 1) { - setUpdated(days + ' days ago'); - } else { - setUpdated(days + ' day ago'); - } - } else if (difftime > secondsInHour) { - const hours = parseInt(difftime / secondsInHour); - if (hours > 1) { - setUpdated(hours + ' hours ago'); - } else { - setUpdated(hours + ' hour ago'); - } - } else { - const minutes = parseInt(difftime % secondsInHour / secondsInMinute); - if (minutes > 1) { - setUpdated(minutes + ' minutes ago'); - } else { - setUpdated('1 minute ago'); + if (res.status === 200) { + setTagContent('workshopping'); } + } catch (error) { + confirmFailure(); + setButtonContent(`REACTIVATE THIS IDEA`); } - - }, []); + }; return ( - - - - idea_image - - - - - - - {tag} - - - {cards.ideaName} - + + + - {cards.comments.length > 0 ? ( - - commentSvg - + + + + + + {cards.ideaName} + -  COMMENTS: {cards.comments.length} - + + + - ) : ( - null - )} - - - timeSvg - -  UPDATED: {updated} - + + - {cardType == "mine" ? ( - - - WORKSHOP THIS IDEA - - - ) : ( - - - HELP THIS IDEA - - - )} - + {isAuthenticated ? + setLiked((prev) => !prev)} + filled={liked} + text={liked ? 1 : ''} + > - + : null} + + + {tagContent == "archived" ? ( + + {buttonContent} + + ) : ( + + + {buttonContent} + + + )} + - ) + ); } -export default IdeaCard +export default IdeaCard; diff --git a/apps/ideaspace/src/components/common/IdeaCard/IdeaCardComment.js b/apps/ideaspace/src/components/common/IdeaCard/IdeaCardComment.js new file mode 100644 index 000000000..0398047b1 --- /dev/null +++ b/apps/ideaspace/src/components/common/IdeaCard/IdeaCardComment.js @@ -0,0 +1,25 @@ +import React from 'react'; +import { atoms } from '@devlaunchers/components/src/components'; +import commentSvg from '../../../images/comment.svg'; + +const IdeaCardComment = ({ + commentLength +}) => { + + return ( + + commentSvg + {commentLength > 0 ? ( + +  COMMENTS: {commentLength} + + ) : ( + +  NO COMMENT YET + + )} + + ) +}; + +export default IdeaCardComment; \ No newline at end of file diff --git a/apps/ideaspace/src/components/common/IdeaCard/IdeaCardImg.js b/apps/ideaspace/src/components/common/IdeaCard/IdeaCardImg.js new file mode 100644 index 000000000..4ade453e6 --- /dev/null +++ b/apps/ideaspace/src/components/common/IdeaCard/IdeaCardImg.js @@ -0,0 +1,24 @@ +import React from 'react'; +import Link from 'next/link'; +import { + ImgButton, +} from './StyledIdeaCard'; + +const IdeaCardImg = ({ + cardId +}) => { + + return ( + + + idea_image + + + ) +}; + +export default IdeaCardImg; \ No newline at end of file diff --git a/apps/ideaspace/src/components/common/IdeaCard/IdeaCardTag.js b/apps/ideaspace/src/components/common/IdeaCard/IdeaCardTag.js new file mode 100644 index 000000000..8588961bb --- /dev/null +++ b/apps/ideaspace/src/components/common/IdeaCard/IdeaCardTag.js @@ -0,0 +1,37 @@ +import React, { useState } from 'react'; +import theme from '@devlaunchers/components/styles/theme'; +import { + StatuBox, +} from './StyledIdeaCard'; + +const IdeaCardTag = ({ + status +}) => { + const [tag, setTag] = useState(''); + const [tagColor, setTagColor] = useState(''); + + React.useEffect(() => { + if (status == "submitted"){ + setTag("submitted"); + setTagColor(theme.colors.LIGHT_BLUE_100); + } else if (status == "applying"){ + setTag("applying"); + setTagColor(theme.colors.YELLOW_100); + } else if (status == "approved"){ + setTag("approved"); + setTagColor(theme.colors.SUCCESS_100); + } else if (status == "archived"){ + setTag("archived"); + setTagColor(theme.colors.BLUE_100); + } else { + setTag("workshopping"); + setTagColor(theme.colors.ORANGE_100); + } + }, [status]); + + return ( + {tag} + ) +}; + +export default IdeaCardTag; \ No newline at end of file diff --git a/apps/ideaspace/src/components/common/IdeaCard/IdeaCardUpdated.js b/apps/ideaspace/src/components/common/IdeaCard/IdeaCardUpdated.js new file mode 100644 index 000000000..6c5b305a7 --- /dev/null +++ b/apps/ideaspace/src/components/common/IdeaCard/IdeaCardUpdated.js @@ -0,0 +1,69 @@ +import React, { useState } from 'react'; +import { atoms } from '@devlaunchers/components/src/components'; +import timeSvg from '../../../images/time.svg'; + +const IdeaCardUpdated = ({ + updatedAt +}) => { + const [updated, setUpdated] = useState(''); + + React.useEffect(() => { + const toBeOneSecond = 1000; + const secondsInDay = 86400; + const secondsInMonth = secondsInDay * 30; + const secondsInYear = secondsInMonth * 12; + const secondsInHour = 3600; + const secondsInMinute = 60; + + const difftime = (new Date() - new Date(updatedAt)) / toBeOneSecond; + + if (difftime > secondsInYear) { + const years = parseInt(difftime / secondsInYear); + if (years > 1) { + setUpdated(years + ' years ago'); + } else { + setUpdated(years + ' year ago'); + } + } else if (difftime > secondsInMonth) { + const months = parseInt(difftime / secondsInMonth); + if (months > 1) { + setUpdated(months + ' months ago'); + } else { + setUpdated(months + ' month ago'); + } + } else if (difftime > secondsInDay) { + const days = parseInt(difftime / secondsInDay); + if (days > 1) { + setUpdated(days + ' days ago'); + } else { + setUpdated(days + ' day ago'); + } + } else if (difftime > secondsInHour) { + const hours = parseInt(difftime / secondsInHour); + if (hours > 1) { + setUpdated(hours + ' hours ago'); + } else { + setUpdated(hours + ' hour ago'); + } + } else { + const minutes = parseInt(difftime % secondsInHour / secondsInMinute); + if (minutes > 1) { + setUpdated(minutes + ' minutes ago'); + } else { + setUpdated('1 minute ago'); + } + } + + }, []); + + return ( + + timeSvg + +  UPDATED: {updated} + + + ) +}; + +export default IdeaCardUpdated; \ No newline at end of file diff --git a/apps/ideaspace/src/components/common/IdeaCard/StyledIdeaCard.js b/apps/ideaspace/src/components/common/IdeaCard/StyledIdeaCard.js index 699b9b4a2..91d00fb8d 100644 --- a/apps/ideaspace/src/components/common/IdeaCard/StyledIdeaCard.js +++ b/apps/ideaspace/src/components/common/IdeaCard/StyledIdeaCard.js @@ -1,13 +1,25 @@ import styled from "styled-components"; +import { atoms } from '@devlaunchers/components/src/components'; + +export const IdeaCardButton = styled((props)=> + +)` + margin: 0rem 2rem 1.5rem; +`; export const ImgButton = styled.button` cursor: pointer; border: 0 none; padding: 0; + margin-top: -3rem; width: 22.5rem; `; -export const StatuBox = styled.span` +export const StatuBox = styled.div` display: flex; flex-direction: row; justify-content: center; @@ -16,8 +28,8 @@ background: #C0E1EB; border-radius: 1rem; z-index: 1; position: relative; -left: -0.5rem; -top: -15rem; +left: 1.5rem; +top: 1rem; padding: 0.2rem 0.5rem; font-family: 'Nunito Sans'; diff --git a/apps/ideaspace/src/components/common/IdeaForm/EditionButton.js b/apps/ideaspace/src/components/common/IdeaForm/EditionButton.js index 4e6e196b0..f9dadfed2 100644 --- a/apps/ideaspace/src/components/common/IdeaForm/EditionButton.js +++ b/apps/ideaspace/src/components/common/IdeaForm/EditionButton.js @@ -1,7 +1,7 @@ import React from 'react'; import { atoms } from '@devlaunchers/components/src/components'; -const EditionButton = ({clickHandlerButton, sending}) => { +const EditionButton = ({clickHandlerButton, sending, disabling}) => { const goBack = () => { clickHandlerButton("back"); }; @@ -20,6 +20,7 @@ const EditionButton = ({clickHandlerButton, sending}) => { buttonSize='standard' buttonType='primary' type='submit' + disabled={disabling} > {' '}{sending === true ? 'Wait' : 'Save edits'}{' '} diff --git a/apps/ideaspace/src/components/common/IdeaForm/IdeaForm.js b/apps/ideaspace/src/components/common/IdeaForm/IdeaForm.js index 006d1e8bf..943b68a03 100644 --- a/apps/ideaspace/src/components/common/IdeaForm/IdeaForm.js +++ b/apps/ideaspace/src/components/common/IdeaForm/IdeaForm.js @@ -1,12 +1,13 @@ import React, { useState } from 'react'; import { Field, Form, Formik, FormikHelpers, useFormikContext } from 'formik'; -import * as Yup from 'yup'; import { atoms, organisms } from '@devlaunchers/components/src/components'; -import Popballoon from '../Popover/Popover'; import popoverSvg from '../../../images/popover.svg'; import SubmissionButton from './SubmissionButton'; import EditionButton from './EditionButton'; +import Dropdown from '@devlaunchers/components/components/organisms/Dropdown'; +import useResponsive from '@devlaunchers/components/src/hooks/useResponsive'; +import Checkbox from '@devlaunchers/components/src/components/Checkbox/Checkbox' const IdeaForm = ({ initialValues, @@ -16,8 +17,11 @@ const IdeaForm = ({ formButton, sending, clickHandler -}) => { - +},props) => { + const { errors } = props; + const [disabling, setDisabling] = React.useState(true); + const { isMobile } = useResponsive(); + const compareValuesToInitial = (values) => { const name = Object.keys(values); for (let i = 0; i < name.length; i++) { @@ -33,13 +37,14 @@ const IdeaForm = ({ React.useEffect(() => { if (compareValuesToInitial(values)) { unsavedHandler(true); + setDisabling(false); } else { unsavedHandler(false); + setDisabling(true); } }, [values]); return null; }; - return ( @@ -49,7 +54,6 @@ const IdeaForm = ({ onSubmit={submitHandler} enableReinitialize > - {({ errors, setFieldValue, touched }) => (
      @@ -78,7 +82,7 @@ const IdeaForm = ({ - what level of involvement do you want to have after submission? + What Level of Involvement Do You Want to Have After Submission? * - - - - - - - - + + { + const selectedOptions = Object.entries(value).find(([key,value])=> value === true) + if (selectedOptions){ + setFieldValue('involveLevel', selectedOptions[0]) + } + }} + /> submit_image + {errors.involveLevel} - After submitting your idea will be reviewed and enter the workshopping stage! + After submitting your idea, it will be reviewed and enter the workshopping stage! - - - + + + +  I have read and agree to the Terms and Conditions. * + @@ -188,6 +214,7 @@ const IdeaForm = ({ )} @@ -203,4 +230,4 @@ const IdeaForm = ({ ); }; -export default IdeaForm; \ No newline at end of file +export default IdeaForm; diff --git a/apps/ideaspace/src/components/common/Popover/Popover.js b/apps/ideaspace/src/components/common/Popover/Popover.js deleted file mode 100644 index 3ee46fdf8..000000000 --- a/apps/ideaspace/src/components/common/Popover/Popover.js +++ /dev/null @@ -1,49 +0,0 @@ -import React from 'react'; -import popoverSvg from '../../../images/popover.svg'; -import { - Popover, Typography -} from '@mui/material'; - -const Popballoon = ({ - content, -}) => { - - const [anchorEl, setAnchorEl] = React.useState(null); - - const handleClick = (event) => { - setAnchorEl(event.currentTarget); - }; - - const handleClose = () => { - setAnchorEl(null); - }; - - const open = Boolean(anchorEl); - const id = open ? 'simple-popover' : undefined; - - - return ( -
      - submit_image - - - {content} - -
      - ); -}; - -export default Popballoon; \ No newline at end of file diff --git a/apps/ideaspace/src/components/common/SignInButton/SignInButton.js b/apps/ideaspace/src/components/common/SignInButton/SignInButton.js index dc6b3bd1c..68182ad69 100644 --- a/apps/ideaspace/src/components/common/SignInButton/SignInButton.js +++ b/apps/ideaspace/src/components/common/SignInButton/SignInButton.js @@ -3,7 +3,7 @@ import { MenuButton } from "./StyledSignInButton"; -export default function SignInButton({redirectUrl="https://devlaunchers.org/users/me"}) { +export default function SignInButton({redirectUrl=(process.env.NEXT_PUBLIC_FRONT_END_URL + "/users/me")}) { return ( Sign In{" "} diff --git a/apps/ideaspace/src/components/modules/BrowseIdeas/BrowseIdeas.js b/apps/ideaspace/src/components/modules/BrowseIdeas/BrowseIdeas.js index 6a2b07768..96e7002a4 100644 --- a/apps/ideaspace/src/components/modules/BrowseIdeas/BrowseIdeas.js +++ b/apps/ideaspace/src/components/modules/BrowseIdeas/BrowseIdeas.js @@ -1,23 +1,26 @@ -import React from 'react' -import CircularIndeterminateLoader from '../Loader/CircularIndeterminateLoader' -import axios from "axios"; +import React from 'react'; +import CircularIndeterminateLoader from '../Loader/CircularIndeterminateLoader'; import { atoms } from '@devlaunchers/components/src/components'; import IdeaCard from '../../common/IdeaCard/IdeaCard'; import BackButton from '../../common/BackButton/BackButton'; import Dropdown from '@devlaunchers/components/components/organisms/Dropdown'; +import { agent } from '@devlaunchers/utility'; +import { cleanDataList } from '../../../utils/StrapiHelper'; +import useResponsive from '@devlaunchers/components/src/hooks/useResponsive'; import { PageWrapper, - HeadWapper, - Headline, StyledRanbow, IdeaCardWrapper, - FilterDiv + FilterDiv, } from './StyledBrowseIdeas'; +import { HeadWapper, Headline } from '../../common/CommonStyles'; function BrowseIdeas() { const [cards, setCards] = React.useState([]); + const [sourceCards, setSourceCards] = React.useState([]); const [loading, setLoading] = React.useState(true); + const { isMobile } = useResponsive(); const sortingConfigs = [ { @@ -66,26 +69,53 @@ function BrowseIdeas() { setCards(cardsClone); }; - React.useEffect(() => { - axios - .get(`${process.env.NEXT_PUBLIC_STRAPI_URL}/idea-cards`, { - withCredentials: true, - }) - .then((response) => { - const getCards = response.data.map((item) => { - return { - ...item, - mostRecentCommentTime: new Date( - item.comments[0]?.updated_at - ).getTime(), - }; - }); - - setLoading(false); - setCards(getCards); - }); + React.useEffect(async () => { + const ideaCards = cleanDataList(await agent.Ideas.get( + new URLSearchParams(`populate=*&pagination[pageSize]=1000`))); + + const getCards = ideaCards.map((item) => { + if (item?.comments?.data) { + item.comments = cleanDataList(item.comments.data); + + const recentCommentedTime = item.comments.length > 0 ? new Date( + item.comments[0]?.updatedAt + ) : new Date(item.updatedAt); + + return { + ...item, + mostRecentCommentTime: recentCommentedTime + }; + } + return { + ...item, + mostRecentCommentTime: new Date(item.updatedAt)?.getTime(), + }; + }); + + setLoading(false); + setCards(getCards); }, []); + React.useEffect(() => { + setCards(sourceCards.filter((item) => item?.status !== 'archived')); + if (defaultShownCardNum >= sourceCards.length) { + setButtonDisplay({ display: 'none' }); + } else { + setButtonDisplay({ display: '' }); + } + }, [sourceCards]); + + const defaultShownCardNum = 30; + const [buttonDisplay, setButtonDisplay] = React.useState(); + const [displayCardAmount, setDisplayCardAmount] = + React.useState(defaultShownCardNum); + const loadMore = () => { + setDisplayCardAmount(displayCardAmount + defaultShownCardNum); + if (displayCardAmount + defaultShownCardNum >= cards.length) { + setButtonDisplay({ display: 'none' }); + } + }; + return ( <> @@ -94,9 +124,13 @@ function BrowseIdeas() { - - Want to help developing an idea?
      - Check out these ideas submitted by other Dev Launchers! + + Want to help develop an idea? +
      + + {' '} + Check out these ideas submitted by other Dev Launchers! +
      @@ -105,47 +139,52 @@ function BrowseIdeas() { ) : (
      - - { - sortCards( - Object.entries(value).filter(([key, value]) => { - return value; - })[0][0] - ); - }} - title="Sort By" - type="radio" - /> - + + { + sortCards( + Object.entries(value).filter(([key, value]) => { + return value; + })[0][0] + ); + }} + title="Sort By" + type="radio" + /> + - {cards.map((item) => { + {cards.slice(0, displayCardAmount).map((item) => { return ( - + ); })} + + + load more +
      )} diff --git a/apps/ideaspace/src/components/modules/BrowseIdeas/StyledBrowseIdeas.js b/apps/ideaspace/src/components/modules/BrowseIdeas/StyledBrowseIdeas.js index 00d8788ba..09140022f 100644 --- a/apps/ideaspace/src/components/modules/BrowseIdeas/StyledBrowseIdeas.js +++ b/apps/ideaspace/src/components/modules/BrowseIdeas/StyledBrowseIdeas.js @@ -2,43 +2,19 @@ import styled from "styled-components"; export const PageWrapper = styled.section` background-color: #FFFFFF; - padding: 0rem calc((100% - 75.5rem)/2) 5rem calc((100% - 75.5rem)/2); + padding: 0rem calc((100% - 75.5rem)/2) 3.5rem calc((100% - 75.5rem)/2); @media (max-width: 1278px) { - padding: 0rem calc((100% - 70.5rem)/2) 5rem calc((100% - 70.5rem)/2); + padding: 0rem calc((100% - 70.5rem)/2) 3.5rem calc((100% - 70.5rem)/2); } @media (max-width: 1192px) { - padding: 0rem calc((100% - 46.5rem)/2) 5rem calc((100% - 46.5rem)/2); + padding: 0rem calc((100% - 46.5rem)/2) 3.5rem calc((100% - 46.5rem)/2); } @media (max-width: 810px) { padding: 0rem 2rem 5rem 2rem; } `; -export const HeadWapper = styled.div` - background-color: #FFFFFF; - padding: 6rem 1rem 4rem 1rem; - - @media (max-width: 1712px) { - padding: 3rem 1rem 2rem 1rem; - } - - @media (max-width: 529px) { - padding: 5rem 1rem 1rem 1rem; - } -`; - -export const Headline = styled.div` - font-family: 'Abel'; - font-style: normal; - font-weight: 400; - font-size: 4rem; - line-height: 68px; - text-align: center; - letter-spacing: -0.02em; - color: #1C1C1C; -`; - export const StyledRanbow = styled.div` margin: 1.3rem auto 0 auto; max-width: 21rem; @@ -51,10 +27,10 @@ flex-direction: row; flex-wrap: wrap; column-gap: 3.8rem; row-gap: 2rem; -margin-top: 1.5rem; +margin: 1.5rem auto; @media (max-width: 1278px) { - column-gap: 1.5rem; + column-gap: 1.2rem; } `; diff --git a/apps/ideaspace/src/components/modules/DashboardPage/DashboardPage.js b/apps/ideaspace/src/components/modules/DashboardPage/DashboardPage.js index 51a630f7b..33f0fa36a 100644 --- a/apps/ideaspace/src/components/modules/DashboardPage/DashboardPage.js +++ b/apps/ideaspace/src/components/modules/DashboardPage/DashboardPage.js @@ -1,11 +1,12 @@ import React from 'react'; -import axios from "axios"; import { useUserDataContext } from '@devlaunchers/components/context/UserDataContext'; import { atoms } from '@devlaunchers/components/src/components'; import SignInSection from '../../common/SignInSection/SignInSection'; -import CircularIndeterminateLoader from '../Loader/CircularIndeterminateLoader' +import CircularIndeterminateLoader from '../Loader/CircularIndeterminateLoader'; import Stats from './Stats/Stats'; import Ideas from './Ideas/Ideas'; +import { cleanDataList, cleanData } from '../../../utils/StrapiHelper'; +import { agent } from '@devlaunchers/utility'; import { HeadWapper, @@ -15,42 +16,36 @@ import { } from './StyledDashboardPage'; function DashboardPage() { - - let { userData, setUserData, isAuthenticated } = useUserDataContext(); - if (process.env.NEXT_PUBLIC_NAME == 'DEVELOPMENT') { - isAuthenticated = true; - - React.useEffect(() => { - setUserData({ ...userData, id: 2 }); - }, []); - } + let { userData, isAuthenticated } = useUserDataContext(); const [loading, setLoading] = React.useState(true); const [sourceCards, setSourceCards] = React.useState([]); const [cards, setCards] = React.useState([]); - React.useEffect(() => { - { - isAuthenticated ? - axios - .get(`${process.env.NEXT_PUBLIC_STRAPI_URL}/idea-cards`, { - withCredentials: true, - }) - .then((response) => { - const cards = response.data.map((item) => { - return { - ...item, - mostRecentCommentTime: new Date( - item.comments[0]?.updated_at - ).getTime(), - }; - }); + React.useEffect(async () => { + if (isAuthenticated) { + const data = cleanDataList( + await agent.Ideas.get(new URLSearchParams(`populate=deep`)) + ); + + const allCards = data.map((item) => { + if (item.comments === undefined) item.comments = []; + else item.comments = cleanDataList(item.comments.data); - setLoading(false); - setSourceCards(cards); - }) - : - '' + if (item.author.data !== null) { + item.author = cleanData(item.author.data); + } + + return { + ...item, + mostRecentCommentTime: new Date( + item.comments[0]?.updated_at + ).getTime(), + }; + }); + + setLoading(false); + setSourceCards(allCards); } }, [isAuthenticated]); @@ -65,15 +60,17 @@ function DashboardPage() { - + Everything about your ideas in one place. {!isAuthenticated ? ( ) : ( @@ -81,18 +78,12 @@ function DashboardPage() { ) : ( <> - - - + + )} - ) - } + )} ); } diff --git a/apps/ideaspace/src/components/modules/EditIdea/EditIdea.js b/apps/ideaspace/src/components/modules/EditIdea/EditIdea.js index 6e1131b15..e3d975218 100644 --- a/apps/ideaspace/src/components/modules/EditIdea/EditIdea.js +++ b/apps/ideaspace/src/components/modules/EditIdea/EditIdea.js @@ -1,47 +1,57 @@ -import React, { useState } from 'react'; -import axios from 'axios'; +import { useState, useEffect } from 'react'; import { useRouter } from 'next/router'; import { useUserDataContext } from '@devlaunchers/components/context/UserDataContext'; - +import { atoms } from '@devlaunchers/components/src/components'; +import { agent } from '@devlaunchers/utility'; +import { cleanData } from '../../../utils/StrapiHelper'; import SignInSection from '../../common/SignInSection/SignInSection'; import BackButton from '../../common/BackButton/BackButton'; import IdeaForm from '../../common/IdeaForm/IdeaForm'; import useConfirm from '../../common/DialogBox/DialogBox'; -import getNotice from '../../common/DialogBox/NoticeBox'; import * as Yup from 'yup'; -import { atoms,organisms } from '@devlaunchers/components/src/components'; - -import { - HeadWapper, - Headline, - StyledRanbow, -} from './StyledEditIdea'; - -function SubmissionForm() { - let { userData, setUserData, isAuthenticated } = useUserDataContext(); - if (process.env.NEXT_PUBLIC_NAME == 'DEVELOPMENT') { - isAuthenticated = true; - - React.useEffect(() => { - setUserData({ ...userData, id: 30 }); - }, []); - } + +import { HeadWapper, Headline, StyledRanbow } from './StyledEditIdea'; + +function EditIdea() { + let { userData, isAuthenticated } = useUserDataContext(); const router = useRouter(); const { ideaId } = router.query; - const [sending, setSending] = React.useState(false); + const [sending, setSending] = useState(false); const [unsavedChanges, setunsavedChanges] = useState(false); + const [getError, setGetError] = useState(false); const [Dialog, confirmLeave] = useConfirm( - 'You have unsaved changes', + ['You have unsaved changes', '', ''], 'Are you sure you want to discard the changes and leave?', + ['alternative primary', 'CANCEL', 'LEAVE'] ); const [urrl, setUrrl] = useState(''); - const [Notice, confirmNotice] = getNotice( - 'Idea updated successfully', + const [UpdateSucceed, confirmSucceed] = useConfirm( + ['Idea updated successfully', '', ''], + '', + ['primary', 'got it'] + ); + + const [UpdateFailure, confirmFailure] = useConfirm( + ['Unable to update your idea', '', ''], + 'Please try again.', + ['primary', 'close'] + ); + + const [NotAuthor, confirmNotAuthor] = useConfirm( + ['This is not your idea.', '', ''], + '', + ['primary', 'close'] + ); + + const [ArchivedIdea, confirmArchive] = useConfirm( + ['This idea has been archived.', '', ''], + 'To workshop on it, you need to reactivate it first.', + ['primary', 'got it'] ); - const [card, setCard] = React.useState({ + const [card, setCard] = useState({ ideaName: '', tagline: '', description: '', @@ -51,57 +61,75 @@ function SubmissionForm() { extraInfo: '', involveLevel: '', status: '', - hourCommitmentMin: 0, - hourCommitmentMax: 0, - difficultyLevel: 'Beginner', }); - React.useEffect(() => { + const rejectAuthor = async () => { + if (!(await confirmArchive())) { + router.push(`/ideaspace/workshop/${ideaId}`); + } + }; + + const rejectUser = async () => { + if (!(await confirmNotAuthor())) { + window.history.back(-1); + } + }; + + useEffect(async () => { if (ideaId) { - axios.get(`${process.env.NEXT_PUBLIC_STRAPI_URL}/idea-cards/${ideaId}`) - .then(response => { - if (response.status === 200) { - if(userData.id !== 0){ - if(response.data.author.id == userData.id){ - setCard(response.data); - }else{ - alert("This is not your idea. You can't edit it."); - window.history.back(-1); - } - } + const idea = cleanData( + await agent.Ideas.getIdea(ideaId, new URLSearchParams('populate=*')) + ); + if (!idea || !idea.id || idea.id == 0) { + setGetError(true); + return; + } + + if (userData.id !== 0) { + if (idea.author.data.id === userData.id) { + if (idea.status == 'archived') { + rejectAuthor(); } - }) + + setCard(idea); + } else { + rejectUser(); + } + } } - }, [ideaId,userData.id]); + }, [ideaId, userData.id]); const SignupSchema = Yup.object().shape({ - ideaName: Yup.string().required('Idea Name is Required.'), - description: Yup.string().required('Idea Description is Required.'), - experience: Yup.string().required('Experience is Required.'), - features: Yup.string().required('Idea Feature is Required.'), + ideaName: Yup.string().trim().required('Idea Name is Required.'), + description: Yup.string().trim().required('Idea Description is Required.'), + experience: Yup.string().trim().required('Experience is Required.'), + features: Yup.string().trim().required('Idea Feature is Required.'), + involveLevel: Yup.string().required('Level of involvement is Required.'), }); const submitHandler = async (values) => { - if (values == card) { - alert("nothing chage"); - return; - } + values['ideaName'] = values['ideaName'].trim(); + values['tagline'] = values['tagline'].trim(); + values['description'] = values['description'].trim(); + values['targetAudience'] = values['targetAudience'].trim(); + values['features'] = values['features'].trim(); + values['experience'] = values['experience'].trim(); + values['extraInfo'] = values['extraInfo'].trim(); setSending(true); - const res = await axios.put( - `${process.env.NEXT_PUBLIC_STRAPI_URL}/idea-cards/${ideaId}`, - values - ); + try { + const data = cleanData(await agent.Ideas.put(ideaId, values)); - if (res.status === 200) { - setunsavedChanges(false); - if (await confirmNotice()){ - setUrrl(`/ideaspace/workshop/${res.data.id}`); + if (data.ideaName) { + setunsavedChanges(false); + if (await confirmNotice()) { + setUrrl(`/ideaspace/workshop/${data.id}`); + } } - } else { - alert('Unable to update your idea.'); + } catch (error) { setSending(false); setunsavedChanges(true); + confirmFailure(); } }; @@ -111,29 +139,28 @@ function SubmissionForm() { } else { setSending(false); } - } + }; - React.useEffect(() => { + useEffect(() => { window.onbeforeunload = () => { if (unsavedChanges) { return 'You have unsaved changes. Do you really want to leave?'; } }; - - if (unsavedChanges && urrl == '' ) { + if (unsavedChanges && urrl == '') { const routeChangeStart = (url) => { handleDialog(url); router.events.emit('routeChangeError'); throw 'Abort route change. Please ignore this error.'; - } + }; router.events.on('routeChangeStart', routeChangeStart); return () => { router.events.off('routeChangeStart', routeChangeStart); }; } else if (urrl !== '') { - if (urrl == 'back'){ + if (urrl == 'back') { window.history.back(-1); - }else{ + } else { router.push(urrl); } } @@ -145,49 +172,54 @@ function SubmissionForm() { } else { window.history.back(-1); } - } + }; - return ( - <> - - - Dev Ideas - - - - - - Have an idea for a development project?
      - Share your idea with us! -
      -
      - - {!isAuthenticated ? ( - - ) : ( - <> - - - ; + } else { + return ( + <> + + Dev Ideas + + + + + + Have an idea for a development project? +
      + Share your idea with us! +
      +
      + + {!isAuthenticated ? ( + - - )} - - ); - + ) : ( + <> + + + + + + + + )} + + ); + } } -export default SubmissionForm; +export default EditIdea; diff --git a/apps/ideaspace/src/components/modules/IdeasBetaFeedbackModal/IdeasBetaFeedbackModal.js b/apps/ideaspace/src/components/modules/IdeasBetaFeedbackModal/IdeasBetaFeedbackModal.js index 070cdf6e9..49d5cf3af 100644 --- a/apps/ideaspace/src/components/modules/IdeasBetaFeedbackModal/IdeasBetaFeedbackModal.js +++ b/apps/ideaspace/src/components/modules/IdeasBetaFeedbackModal/IdeasBetaFeedbackModal.js @@ -37,22 +37,12 @@ const ModalCustomStyles = { const IdeasBetaFeedbackModal = () => { const [showModal, setShowModal] = useState(false); - function refreshIsIdeaSpaceVisited() { - localStorage.setItem('dl_Ideas_isVisited', true); - } - - useEffect(() => { - const isVisited = localStorage.dl_Ideas_isVisited || false; - if (!isVisited) openModal(); - }); - function openModal() { setShowModal(true); } function closeModal() { setShowModal(false); - refreshIsIdeaSpaceVisited(); } Modal.setAppElement('#__next'); diff --git a/apps/ideaspace/src/components/modules/SaveIdea/SaveIdea.js b/apps/ideaspace/src/components/modules/SaveIdea/SaveIdea.js new file mode 100644 index 000000000..310515bdd --- /dev/null +++ b/apps/ideaspace/src/components/modules/SaveIdea/SaveIdea.js @@ -0,0 +1,38 @@ +import React from 'react'; +import SaveButton, { SaveButtonProps } from '@devlaunchers/components/src/components/molecules/InteractionButtons/SaveButton'; +import { StyledSaveIdea } from './StyledSaveIdea'; +import { agent } from '@devlaunchers/utility'; + + +const SaveIdea = ({savedCards, setSavedCards, id, user}) =>{ + const savedIdea = savedCards.includes(id) + + const handleSaved = async () =>{ + const data = { + objectId: id, + objectType: 1, + user: user + } + try { + const request = await agent.Saves.post(data) + console.log(request) + } catch(error){ + console.log("error occured", error) + } + } + + return( + + { + setSavedCards([...savedCards, id]) + handleSaved() + }} + /> + + ) +} +export default SaveIdea; \ No newline at end of file diff --git a/apps/ideaspace/src/components/modules/SaveIdea/StyledSaveIdea.js b/apps/ideaspace/src/components/modules/SaveIdea/StyledSaveIdea.js new file mode 100644 index 000000000..7152143bd --- /dev/null +++ b/apps/ideaspace/src/components/modules/SaveIdea/StyledSaveIdea.js @@ -0,0 +1,12 @@ +import styled from "styled-components"; + +export const StyledSaveIdea = styled.div` + font-weight: 400; + display: flex; + flex-direction: row; + justify-content: center; + z-index: 1; + position: relative; + left: 5rem; + top: 1rem; +`; diff --git a/apps/ideaspace/src/components/modules/SubmissionForm/StyledSubmissionForm.js b/apps/ideaspace/src/components/modules/SubmissionForm/StyledSubmissionForm.js index afaef9866..77866e4a5 100644 --- a/apps/ideaspace/src/components/modules/SubmissionForm/StyledSubmissionForm.js +++ b/apps/ideaspace/src/components/modules/SubmissionForm/StyledSubmissionForm.js @@ -1,29 +1,5 @@ import styled from "styled-components"; -export const HeadWapper = styled.div` - background-color: #FFFFFF; - padding: 6rem 1rem 4rem 1rem; - - @media (max-width: 1712px) { - padding: 3rem 1rem 2rem 1rem; - } - - @media (max-width: 529px) { - padding: 5rem 1rem 1rem 1rem; - } -`; - -export const Headline = styled.div` - font-family: 'Abel'; - font-style: normal; - font-weight: 400; - font-size: 4rem; - line-height: 68px; - text-align: center; - letter-spacing: -0.02em; - color: #1C1C1C; -`; - export const StyledRanbow = styled.div` margin: 1.3rem auto 0 auto; max-width: 24.8rem; diff --git a/apps/ideaspace/src/components/modules/SubmissionForm/SubmissionForm.js b/apps/ideaspace/src/components/modules/SubmissionForm/SubmissionForm.js index 5bf26734d..c4c18061a 100644 --- a/apps/ideaspace/src/components/modules/SubmissionForm/SubmissionForm.js +++ b/apps/ideaspace/src/components/modules/SubmissionForm/SubmissionForm.js @@ -1,40 +1,36 @@ -import React, { useState } from 'react'; -import axios from 'axios'; +import { useState, useEffect } from 'react'; import { useRouter } from 'next/router'; import { useUserDataContext } from '@devlaunchers/components/context/UserDataContext'; - +import { agent } from '@devlaunchers/utility'; +import { atoms } from '@devlaunchers/components/src/components'; +import { cleanData } from '../../../utils/StrapiHelper'; import SignInSection from '../../common/SignInSection/SignInSection'; import BackButton from '../../common/BackButton/BackButton'; import IdeaForm from '../../common/IdeaForm/IdeaForm'; import useConfirm from '../../common/DialogBox/DialogBox'; import * as Yup from 'yup'; -import { atoms } from '@devlaunchers/components/src/components'; - -import { - HeadWapper, - Headline, - StyledRanbow, -} from './StyledSubmissionForm'; +import { StyledRanbow } from './StyledSubmissionForm'; +import { HeadWapper, Headline } from '../../common/CommonStyles'; function SubmissionForm() { - let { userData, setUserData, isAuthenticated } = useUserDataContext(); - if (process.env.NEXT_PUBLIC_NAME == 'DEVELOPMENT') { - isAuthenticated = true; - - React.useEffect(() => { - setUserData({ ...userData, id: 30 }); - }, []); - } + let { userData, isAuthenticated } = useUserDataContext(); const router = useRouter(); const [sending, setSending] = useState(false); const [unsavedChanges, setunsavedChanges] = useState(false); const [Dialog, confirmLeave] = useConfirm( - 'You have unsaved changes', + ['You have unsaved changes','',''], 'Are you sure you want to discard the changes and leave', + ['alternative primary', 'CANCEL', 'LEAVE'] ) const [urrl, setUrrl] = useState(''); + const [CreateFailure, confirmFailure] = useConfirm( + ['Unable to register your idea.','',''], + 'Please try again.', + ['primary', 'close'], + ); + const initialValues = { ideaName: '', tagline: '', @@ -45,34 +41,45 @@ function SubmissionForm() { extraInfo: '', involveLevel: '', status: '', - hourCommitmentMin: 0, - hourCommitmentMax: 0, - difficultyLevel: 'Beginner', }; const SignupSchema = Yup.object().shape({ - ideaName: Yup.string().required('Idea Name is Required.'), - description: Yup.string().required('Idea Description is Required.'), - experience: Yup.string().required('Experience is Required.'), - features: Yup.string().required('Idea Feature is Required.'), + ideaName: Yup.string().trim().required('Idea Name is Required.'), + description: Yup.string().trim().required('Idea Description is Required.'), + experience: Yup.string().trim().required('Experience is Required.'), + features: Yup.string().trim().required('Idea Feature is Required.'), + involveLevel: Yup.string().required('Level of involvement is Required.'), }); + const submitHandler = async (values) => { + values['author'] = userData.id; + values['ideaOwner'] = userData.id; values['status'] = 'workshopping'; + values['ideaName'] = values['ideaName'].trim(); + values['tagline'] = values['tagline'].trim(); + values['description'] = values['description'].trim(); + values['targetAudience'] = values['targetAudience'].trim(); + values['features'] = values['features'].trim(); + values['experience'] = values['experience'].trim(); + values['extraInfo'] = values['extraInfo'].trim(); + values['involveLevel'] = values['involveLevel'].trim() setSending(true); - const res = await axios.post( - `${process.env.NEXT_PUBLIC_STRAPI_URL}/idea-cards/`, - values - ); + try { + const data = cleanData(await agent.Ideas.post(values)); - if (res.status === 200) { - setunsavedChanges(false); - router.push(`workshop/${res.data.id}`); - } else { - alert('Unable to register your idea.'); + if (data.ideaName) { + setunsavedChanges(false); + router.push(`workshop/${data.id}`); + } else { + alert('Unable to register your idea.'); + } + + } catch (error) { setSending(false); setunsavedChanges(true); + confirmFailure(); } }; @@ -83,7 +90,7 @@ function SubmissionForm() { } } - React.useEffect(() => { + useEffect(() => { // For reloading. window.onbeforeunload = () => { if (unsavedChanges) { @@ -103,9 +110,9 @@ function SubmissionForm() { router.events.off('routeChangeStart', routeChangeStart); }; } else if (urrl !== '') { - if (urrl == 'back'){ + if (urrl == 'back') { window.history.back(-1); - }else{ + } else { router.push(urrl); } } @@ -126,7 +133,7 @@ function SubmissionForm() { - @@ -139,11 +146,12 @@ function SubmissionForm() { {!isAuthenticated ? ( ) : ( <> + theme.colors.NEUTRAL_2}; + color: ${({ theme }) => theme.colors.GREYSCALE_OFF_WHITE}; border: 0 none; border-radius: 30px; cursor: pointer; padding: 2rem; font-size: 2rem; font-family: 'Abel'; - background-color: ${({ theme }) => theme.colors.NEUTRAL_1}; + background-color: ${({ theme }) => theme.colors.GREYSCALE_OFF_BLACK}; font-color: 'white'; `; diff --git a/apps/ideaspace/src/components/modules/IdeaspaceNavCard/index.js b/apps/ideaspace/src/components/modules/WelcomePage/IdeaspaceNavCard/index.js similarity index 100% rename from apps/ideaspace/src/components/modules/IdeaspaceNavCard/index.js rename to apps/ideaspace/src/components/modules/WelcomePage/IdeaspaceNavCard/index.js diff --git a/apps/ideaspace/src/components/modules/WelcomePage/StyledWelcomePage.js b/apps/ideaspace/src/components/modules/WelcomePage/StyledWelcomePage.js index 86dc3a6a2..dc8ffca59 100644 --- a/apps/ideaspace/src/components/modules/WelcomePage/StyledWelcomePage.js +++ b/apps/ideaspace/src/components/modules/WelcomePage/StyledWelcomePage.js @@ -22,86 +22,44 @@ export const ButtonArea = styled.aside` } `; -export const Button = styled.button` - width: 100%; - height: 100%; - display: flex; - flex-grow: 1; - flex-direction: column; - justify-content: space-between; - align-items: center; - color: ${({ theme }) => theme.colors.NEUTRAL_2}; - border: 0 none; - border-radius: 30px; - cursor: pointer; - padding: 2rem; - margin: 10px 5px; - font-size: 2rem; - font-family: 'Abel'; - background-color: ${({ theme }) => theme.colors.NEUTRAL_1}; - opacity: 0.85; - @media (orientation: portrait) { - font-size: 1.5rem; - flex-direction: column; - opacity: 0.2; - } - & img { - opacity: 0.25; - object-fit: 'cover'; - height: 80%; - width: 100%; - } -`; - -export const Description = styled.div` - color: black; - text-align: center; - font-family: 'Nunito Sans'; -`; - -export const Header = styled.div` - display: inline-block; - & h1 { - color: black; - text-align: center; - font-family: 'Abel'; - border-bottom: none; - } -`; - -export const BackgroundImage = styled.img` - width: 100%; - height: 50%; -`; - -export const WelcomeMessage = styled.div` - font-family: 'Nunito Sans'; - margin-bottom: 20px; -`; - export const Subheader = styled.div` font-family: 'Nunito Sans'; color: #474747; - margin-bottom: 20px; -`; - -export const GetStartedDiv = styled.div` - font-family: 'Nunito Sans'; - margin-bottom: 10px; + margin: 0.5rem auto; `; export const RocketImage = styled.img` width: 25px; height: 25px; padding-left: 5px; + display: inline-block; `; +export const HeadWapper = styled.div` + padding: 4.5rem 1rem 4rem 1rem; -export const WelcomeNavigationButtonImage = styled.img` - height: 70%; + @media (max-width: 1712px) { + padding: 3rem 1rem 2rem 1rem; + } - @media (orientation: portrait) { - height: 30vw; + @media (max-width: 529px) { + padding: 5rem 1rem 1rem 1rem; } `; +export const Headline = styled.div` + font-family: 'Abel'; + font-style: normal; + font-weight: 400; + font-size: 4rem; + line-height: 68px; + text-align: center; + letter-spacing: -0.02em; + color: #1C1C1C; +`; + +export const StyledRanbow = styled.div` + margin: 1.3rem auto 1.0rem auto; + max-width: 28rem; + height: 5px; +`; \ No newline at end of file diff --git a/apps/ideaspace/src/components/modules/WelcomePage/WelcomePage.js b/apps/ideaspace/src/components/modules/WelcomePage/WelcomePage.js index 94588da60..59c2dae47 100644 --- a/apps/ideaspace/src/components/modules/WelcomePage/WelcomePage.js +++ b/apps/ideaspace/src/components/modules/WelcomePage/WelcomePage.js @@ -1,27 +1,25 @@ import React from 'react'; import Link from 'next/link'; -import RainbowBar from '../../../../../site-projects/src/components/common/RainbowBar'; +import { atoms } from '@devlaunchers/components/src/components'; import handWithLightbulbImage from '../../../images/submit-image.png'; import helpButtonImage from '../../../images/help-image.png'; import bulletinBoardPostItImage from '../../../images/bulletin-board-postit.png'; import rocketImage from '../../../images/logo-monogram.png'; -import IdeaspaceNavCard from '../IdeaspaceNavCard'; +import IdeaspaceNavCard from './IdeaspaceNavCard'; +import IdeaGeneratorButton from './IdeaGeneratorButton'; import { - Description, - ButtonArea, - Header, - WelcomeMessage, + HeadWapper, + Headline, + StyledRanbow, Subheader, - GetStartedDiv, RocketImage, NavButtonArea, + ButtonArea, } from './StyledWelcomePage'; -import IdeaGeneratorButton from '../IdeaGeneratorButton'; - const appGeneratorCardBackgroundColor = `rgba(58,124,165, 0.9), rgba(58,124,165, 0.9))`; const submitIdeaCardBackgroundColor = `rgba(255,127,14, 0.9), rgba(255,127,14, 0.9))`; @@ -35,23 +33,22 @@ const helpExistingIdeaCardDescription = ` Want to help developing an idea? Check function WelcomePage() { return ( <> -
      -

      IdeaSpace (Beta)

      - -
      - - - WELCOME TO THE IDEA PLATFORM! - - Have an idea for a development project? Want to help develop an - idea? Want to generate an idea? - - - LET'S GET STARTED - - - - + + IdeaSpace (Beta) + + + + + + WELCOME TO THE IDEA PLATFORM! + + Have an idea for a development project? Want to help develop an idea? Want to generate an idea? + + LET'S GET STARTED + + + + diff --git a/apps/ideaspace/src/components/modules/WorkshoppingPage/CommentsForm/CommentForm.js b/apps/ideaspace/src/components/modules/WorkshoppingPage/CommentsForm/CommentForm.js index 273c4f44a..f9013ac3e 100644 --- a/apps/ideaspace/src/components/modules/WorkshoppingPage/CommentsForm/CommentForm.js +++ b/apps/ideaspace/src/components/modules/WorkshoppingPage/CommentsForm/CommentForm.js @@ -1,61 +1,64 @@ -import React from 'react'; -import axios from 'axios'; +import { useState } from 'react'; import { - UserNameCommentBox, - UserNameComment, UserComment, UserImageOne, + CommentBox, + // SubmitButton, } from './StyledComments.js'; import { useUserDataContext } from '@devlaunchers/components/context/UserDataContext'; import SignInButton from '../../../common/SignInButton/SignInButton'; - -const MAX_COMMENT_CHARS = 250; +import { agent } from '@devlaunchers/utility'; function CommentForm(props) { + const { userData, isAuthenticated } = useUserDataContext(); const { selectedCard, ...other } = props; - const [charsLeft, setCharsLeft] = React.useState(MAX_COMMENT_CHARS); + const [disabled, setDisabled] = useState(true); + const [textChange, setTextChange] = useState(''); const handleTextChange = (e) => { const text = e.target.value; - props.setHandleTextChange(text); + setTextChange(text); - let characterCount = text.length; - setCharsLeft(MAX_COMMENT_CHARS - characterCount); + if (text.trim() == '') { + setDisabled(true); + } else { + setDisabled(false); + } }; - const handleSubmit = (e) => { + const handleSubmit = async (e) => { + e.preventDefault(); - var data = { author: userData.username, text: props.handleTextChange }; + var data = { author: userData.username, idea_card: selectedCard, text: textChange.trim() }; - axios - .post( - `${process.env.NEXT_PUBLIC_STRAPI_URL}/idea-cards/${selectedCard.id}/comment`, - data - ) - .then((response) => { - if (response.status === 200) { - props.setHandleTextChange(''); - } - }); + try { + const res = await agent.Comments.post(data); + setTextChange(''); + // render the comment in the comment feed + props.renderNewComment(data); + } catch(error) { + console.error(error) + } + + // Refresh the page so that the new comment is displayed: + // window.location.reload(false); + // this.setState( + // {reload: true}, + // () => this.setState({reload: false}) + // ) + + }; - // move to WorkshoppingPage + + // move to WorkshoppingPage? return (
      {isAuthenticated ? ( - - {/* - - */} + - - {/* source: https://codepen.io/patrickwestwood/pen/gPPywv */} -
      - {charsLeft} -
      + // maxlength={MAX_COMMENT_CHARS} + > +
      - ) : (
      Sign in to leave a comment!{' '}
      diff --git a/apps/ideaspace/src/components/modules/WorkshoppingPage/CommentsForm/DisplayComments.js b/apps/ideaspace/src/components/modules/WorkshoppingPage/CommentsForm/DisplayComments.js index 5fe2797dd..9272f901d 100644 --- a/apps/ideaspace/src/components/modules/WorkshoppingPage/CommentsForm/DisplayComments.js +++ b/apps/ideaspace/src/components/modules/WorkshoppingPage/CommentsForm/DisplayComments.js @@ -1,29 +1,26 @@ import React, { useEffect, useState } from 'react'; import Comment from './SingleComment'; +import { cleanDataList } from '../../../../utils/StrapiHelper'; import axios from 'axios'; -function DisplayComments(props) { - - const [data, setData] = useState([]) - - useEffect(() => { - if (props.selectedCard.id != undefined) { - axios.get(`${process.env.NEXT_PUBLIC_STRAPI_URL}/idea-cards/${props.selectedCard.id}`) - .then(response => { - setData((response.data.comments).sort((a, b) => a.published_at < b.published_at ? 1 : -1)) - }) - } - }, [props.selectedCard]) +//create your forceUpdate hook +function useForceUpdate() { + const [value, setValue] = useState(0); // integer state + return () => setValue(value => value + 1); // update state to force render + // A function that increment 👆🏻 the previous state like here + // is better than directly setting `setValue(value + 1)` +} - - const commentNodes = data.map(comment => ( - +function DisplayComments(props) { + const commentNodes = props.comments.map(comment => { + return {comment.text} - )); + }); + return (
      - {data.length ? commentNodes :
      No comments yet!
      } + {props.comments.length ? commentNodes :
      No comments yet!
      }
      ); }; diff --git a/apps/ideaspace/src/components/modules/WorkshoppingPage/CommentsForm/SingleComment.js b/apps/ideaspace/src/components/modules/WorkshoppingPage/CommentsForm/SingleComment.js index 62f7f3e46..4f7cbb5fa 100644 --- a/apps/ideaspace/src/components/modules/WorkshoppingPage/CommentsForm/SingleComment.js +++ b/apps/ideaspace/src/components/modules/WorkshoppingPage/CommentsForm/SingleComment.js @@ -1,24 +1,169 @@ - import { SingleComment, UserImage, - TextContent, SingleCommentContent, SingleCommentButtons } from './StyledComments.js'; +import { LikeButton } from '@devlaunchers/components/src/components/molecules'; +import { useState } from 'react'; +import { useUserDataContext } from '@devlaunchers/components/src/context/UserDataContext.js'; + +// A function to show the date as X hours ago, etc. +// from: https://stackoverflow.com/a/3177838 +function timeSince(date) { + + var seconds = Math.floor((new Date() - date) / 1000); + + var interval = seconds / 31536000; + + if (interval > 1) { + if (Math.floor(interval) == 1) { + return "1 year ago"; + } else { + return Math.floor(interval) + " years ago"; + } + } + interval = seconds / 2592000; + if (interval > 1) { + if (Math.floor(interval) == 1) { + return "1 month ago"; + } else { + return Math.floor(interval) + " months ago"; + } + } + interval = seconds / 86400; + if (interval > 1) { + if (Math.floor(interval) == 1) { + return "1 day ago"; + } else { + return Math.floor(interval) + " days ago"; + } + } + interval = seconds / 3600; + if (interval > 1) { + if (Math.floor(interval) == 1) { + return "1 hour ago"; + } else { + return Math.floor(interval) + " hours ago"; + } + } + interval = seconds / 60; + if (interval > 1) { + if (Math.floor(interval) == 1) { + return "1 minute ago"; + } else { + return Math.floor(interval) + " minutes ago"; + } + } + if (Math.floor(interval) == 1) { + return "1 second ago"; + } else { + return Math.floor(seconds) + " seconds ago"; + } +} + +// Do we need to make a function returning the comment component to keep track of the likes? + +// const SingleCommentComponent = props => ( +// // const [liked, setLiked] = useState(false); +//
      +// +// +//
      +// +//

      {props.author}

      +// {/* get the idea ID from the URL if possible and determine the idea owner (maybe do this in another file) */} +//
      +// +// {/* date of creation here, i.e. "2 days ago" */} +//
      {timeSince(new Date(props.createdAt))}
      +//
      +//
      +//
      +// +//
      +// +//

      {props.children}

      +//
      +// +// +// +//
      +//
      +//
      +//
      +// ); + +function SingleCommentComponent(props) { + const { userData, isAuthenticated } = useUserDataContext(); + const [liked, setLiked] = useState(false); + const [commentLikes, setCommentLikes] = useState([]); + const [state, setState] = useState(false); // to refresh the page when a comment receives a like + + // a function to keep track of the number of likes and when the user clicks the like button for this comment + function handleLikeClick(event) { + if (liked) { + // if there's a like object corresponding to this user and comment, delete it + + // Refresh the page so that the new comment is displayed + setState(true); + + setLiked(false); + } else { + // create a like object using the Like collection from the strapiv4 repo, storing the user ID, the comment ID, and the "Comment" object type + var likeData = { objectId: this.id, objectType: "Comment", users_permission_user: userData.userId }; + + try { + const res = agent.Likes.post(likeData); + } catch(error) { + console.error(error) + } + + event.preventDefault(); + + + try { + + props.setHandleTextChange(''); + } catch(error) { + console.error(error); + } + + // Refresh the page so that the new comment is displayed + setState(true); + + setLiked(true); + } + } -const SingleCommentComponent = props => ( - - + return (
      - -

      {props.author}

      -

      {props.children}

      -
      - - + + +
      + +

      {props.author}

      {props.forIdea.author?.username == props.author?.username ? "idea owner" : ""}
      + {/* get the idea ID from the URL if possible and determine the idea owner (maybe do this in another file) */} +
      + + {/* date of creation here, i.e. "2 days ago" */} +
      {timeSince(new Date(props.createdAt))}
      +
      +
      +
      + +
      + +

      {props.children}

      +
      + + + +
      +
      +
      -
      -); + ) +} export default SingleCommentComponent; \ No newline at end of file diff --git a/apps/ideaspace/src/components/modules/WorkshoppingPage/CommentsForm/StyledComments.js b/apps/ideaspace/src/components/modules/WorkshoppingPage/CommentsForm/StyledComments.js index bb4347892..4b629b518 100644 --- a/apps/ideaspace/src/components/modules/WorkshoppingPage/CommentsForm/StyledComments.js +++ b/apps/ideaspace/src/components/modules/WorkshoppingPage/CommentsForm/StyledComments.js @@ -23,10 +23,10 @@ export const SingleComment = styled.div` export const SingleCommentContent = styled.div` width: 100%; min-width: 430px; - background-color: ${({ theme }) => theme.colors.NEUTRAL_1}; - color: #C4C4C4; + background-color: white; + color: black; border-radius: 25px; - padding: 20px; + padding: 1px; display: inline-flex; @@ -37,15 +37,40 @@ export const SingleCommentContent = styled.div` h3 { margin: 0; padding-right: 5px; - font-size: 12px; - font-style: italic; - font-weight: 1000 ; - color: white; + + color: var(--grey-scale-off-black, #1C1C1C); + font-feature-settings: 'clig' off, 'liga' off; + + font-family: Nunito Sans; + font-size: 16px; + font-style: normal; + font-weight: 400; + line-height: normal; + letter-spacing: 1.28px; + text-transform: uppercase; + } + + h5 { + margin: 0; + padding-right: 5px; + + color: var(--grey-scale-grey, #7F7E7F); + + font-family: Nunito Sans; + font-size: 16px; + font-style: normal; + font-weight: 400; } p { - font-size: 12px; margin: 0; + + color: var(--grey-scale-off-black, #1C1C1C); + font-family: Nunito Sans; + font-size: 16px; + font-style: normal; + font-weight: 400; + line-height: normal; } `; @@ -57,16 +82,21 @@ export const UserNameComment = styled.input` /* width: 100%; */ min-width: 200px; height: 1.2rem; - background-color: ${({ theme }) => theme.colors.NEUTRAL_1}; + background-color: ${({ theme }) => theme.colors.GREYSCALE_OFF_BLACK}; border-radius: 25px; padding: 20px; `; export const UserImage = styled.img` - height: 60px; margin-right: 10px; - border-radius: 50%; + + width: 48px; + height: 48px; + flex-shrink: 0; + + border-radius: 48px; + background: url(), lightgray 0px -1.948px / 100% 153.333% no-repeat; `; @@ -158,6 +188,17 @@ export const UserImageOne = styled.img` `; +export const CommentBox = styled.textarea` + border-radius: 10px; + background-color: #F0EDED; + border: solid 1px #F0EDED; + color: #7F7E7F; + padding: 5px; + margin-left: 10px; + margin-right: 10px; +`; + + export const Idea = styled.div` width: 100%; max-width: 700px; @@ -182,8 +223,8 @@ export const Idea = styled.div` `; export const IdeaName = styled.div` - background-color: ${({ theme }) => theme.colors.NEUTRAL_1}; - border: .2rem solid ${({ theme }) => theme.colors.NEUTRAL_1}; + background-color: ${({ theme }) => theme.colors.GREYSCALE_OFF_BLACK}; + border: .2rem solid ${({ theme }) => theme.colors.GREYSCALE_OFF_BLACK}; width: 100%; height: 50%; border-top-right-radius: 30px; @@ -195,7 +236,7 @@ export const IdeaName = styled.div` export const IdeaProgress = styled.div` - background-color: ${({ theme }) => theme.colors.NEUTRAL_1}; + background-color: ${({ theme }) => theme.colors.GREYSCALE_OFF_BLACK}; width: 100%; height: 50%; padding-top: 30px; @@ -205,7 +246,7 @@ export const IdeaProgress = styled.div` export const Description = styled.div` background-color: white; - border: .2rem solid ${({ theme }) => theme.colors.NEUTRAL_1}; + border: .2rem solid ${({ theme }) => theme.colors.GREYSCALE_OFF_BLACK}; width: 100%; // height: 50%; border-bottom-right-radius: 30px; @@ -237,7 +278,7 @@ export const LeaderImage = styled.img` export const LeaderInfo = styled.div` margin-left: 30px; - background-color: ${({ theme }) => theme.colors.NEUTRAL_1}; + background-color: ${({ theme }) => theme.colors.GREYSCALE_OFF_BLACK}; width: 100%; border-top-right-radius: 30px; border-top-left-radius: 30px; diff --git a/apps/ideaspace/src/components/modules/WorkshoppingPage/DisplayComments.js b/apps/ideaspace/src/components/modules/WorkshoppingPage/DisplayComments.js new file mode 100644 index 000000000..429a9970a --- /dev/null +++ b/apps/ideaspace/src/components/modules/WorkshoppingPage/DisplayComments.js @@ -0,0 +1,29 @@ +import React, { useEffect, useState } from 'react'; +import Comment from './SingleComment'; +import axios from 'axios'; + +export default function DisplayComments(props) { + + const [data, setData] = useState([]) + + useEffect(() => { + if (props.selectedCard.id != undefined) { + axios.get(`${process.env.NEXT_PUBLIC_STRAPI_URL}/idea-cards/${props.selectedCard.id}`) + .then(response => { + setData((response.data.comments).sort((a, b) => a.published_at < b.published_at ? 1 : -1)) + }) + } + }, [props.selectedCard]) + + + const commentNodes = data.map(comment => ( + + {comment.text} + + )); + return ( +
      + {data.length ? commentNodes :
      No comments yet!
      } +
      + ); +}; diff --git a/apps/ideaspace/src/components/modules/WorkshoppingPage/IdeaOverview/DescriptionCard/DescriptionCard.js b/apps/ideaspace/src/components/modules/WorkshoppingPage/IdeaOverview/DescriptionCard/DescriptionCard.js deleted file mode 100644 index 1e2555921..000000000 --- a/apps/ideaspace/src/components/modules/WorkshoppingPage/IdeaOverview/DescriptionCard/DescriptionCard.js +++ /dev/null @@ -1,18 +0,0 @@ -import { - DescriptionList, - Cardheading, - StyledCard, -} from './StyledComponents'; - -export const DescriptionCard = ({ description }) => { - return ( - - - Description - - -
      {description}
      -
      -
      - ); -}; diff --git a/apps/ideaspace/src/components/modules/WorkshoppingPage/IdeaOverview/DescriptionCard/StyledComponents.js b/apps/ideaspace/src/components/modules/WorkshoppingPage/IdeaOverview/DescriptionCard/StyledComponents.js deleted file mode 100644 index 670a513dc..000000000 --- a/apps/ideaspace/src/components/modules/WorkshoppingPage/IdeaOverview/DescriptionCard/StyledComponents.js +++ /dev/null @@ -1,30 +0,0 @@ -import { Card } from '../../../../common/Card/Card' -import styled from '@emotion/styled'; - -export const StyledCard = styled(Card)` - display: flex; - flex-direction: column; - padding: 20px; - padding-left: 30px; - padding-right: 30px; - margin-bottom: 25px; -`; - -export const Cardheading = styled.h3` - display: flex; - align-items: center; - justify-content: start; - margin-left: 8px; - margin-right: 8px; - margin-bottom: 20px; -`; - -export const DescriptionList = styled.div` - display: flex; - flex-direction: column; - align-items: start; - justify-content: center; - padding-bottom: 10px; - margin: 8px; -`; - diff --git a/apps/ideaspace/src/components/modules/WorkshoppingPage/IdeaOverview/IdeaCard/IdeaCard.js b/apps/ideaspace/src/components/modules/WorkshoppingPage/IdeaOverview/IdeaCard/IdeaCard.js index d540cfa42..16d704085 100644 --- a/apps/ideaspace/src/components/modules/WorkshoppingPage/IdeaOverview/IdeaCard/IdeaCard.js +++ b/apps/ideaspace/src/components/modules/WorkshoppingPage/IdeaOverview/IdeaCard/IdeaCard.js @@ -36,7 +36,7 @@ export const IdeaCard = ({ ideaImage, ideaName, ideaTagLine }) => { - + {/* - + */} ); }; diff --git a/apps/ideaspace/src/components/modules/WorkshoppingPage/IdeaOverview/IdeaContentCard/IdeaContentCard.js b/apps/ideaspace/src/components/modules/WorkshoppingPage/IdeaOverview/IdeaContentCard/IdeaContentCard.js new file mode 100644 index 000000000..ce0a59efb --- /dev/null +++ b/apps/ideaspace/src/components/modules/WorkshoppingPage/IdeaOverview/IdeaContentCard/IdeaContentCard.js @@ -0,0 +1,62 @@ +import React, { useState } from 'react'; +import { atoms } from '@devlaunchers/components/src/components'; + +const IdeaContentCard = ({ title, content, sliceLength}) => { + + const [isExpanded, setIsExpanded] = useState(false); + + return ( + + +

      {title}

      +
      + + {content ? ( + <> + {(sliceLength && !isExpanded) ? ( + <> + {content + .slice(0, sliceLength) + .concat("...") + .split('\n') + .map((paras, key) => { + return ( + + + {paras} + + ) + })} + + ) : ( + <> + {content + .split('\n') + .map((paras, key) => { + return ( + + + {paras} + + ) + })} + + )} + + {sliceLength ? ( + + setIsExpanded(!isExpanded)}> + {isExpanded ? 'Collapse Description' : 'Expand Description'} + + + ) : null} + + + ) : null} + +
      + ); +}; + +export default IdeaContentCard; \ No newline at end of file diff --git a/apps/ideaspace/src/components/modules/WorkshoppingPage/IdeaOverview/IdeaFeaturesCard/IdeaFeaturesCard.js b/apps/ideaspace/src/components/modules/WorkshoppingPage/IdeaOverview/IdeaFeaturesCard/IdeaFeaturesCard.js deleted file mode 100644 index 0684e19ea..000000000 --- a/apps/ideaspace/src/components/modules/WorkshoppingPage/IdeaOverview/IdeaFeaturesCard/IdeaFeaturesCard.js +++ /dev/null @@ -1,20 +0,0 @@ -import { - IdeaFeatureList, - Cardheading, - StyledCard -} from './StyledComponents'; - -export const IdeaFeaturesCard = ({ ideaFeature }) => { - return ( - - - Idea features - - - { - ideaFeature === '' ?
      No Features mentioned yet!
      :
      {ideaFeature}
      - } -
      -
      - ); -}; diff --git a/apps/ideaspace/src/components/modules/WorkshoppingPage/IdeaOverview/IdeaFeaturesCard/StyledComponents.js b/apps/ideaspace/src/components/modules/WorkshoppingPage/IdeaOverview/IdeaFeaturesCard/StyledComponents.js deleted file mode 100644 index feb3138b2..000000000 --- a/apps/ideaspace/src/components/modules/WorkshoppingPage/IdeaOverview/IdeaFeaturesCard/StyledComponents.js +++ /dev/null @@ -1,31 +0,0 @@ -import { Card } from '../../../../common/Card/Card' -import styled from '@emotion/styled'; - -export const StyledCard = styled(Card)` - display: flex; - flex-direction: column; - align-items: start; - justify-content: center; - padding: 20px; - padding-left: 30px; - padding-right: 30px; - margin-bottom: 25px; -`; - -export const Cardheading = styled.h3` - display: flex; - align-items: center; - justify-content: start; - margin-left: 8px; - margin-right: 8px; - margin-bottom: 20px; -`; - -export const IdeaFeatureList = styled.div` - display: flex; - flex-direction: column; - align-items: start; - justify-content: center; - padding-bottom: 10px; - margin: 8px; -`; diff --git a/apps/ideaspace/src/components/modules/WorkshoppingPage/IdeaOverview/IdeaOverview.js b/apps/ideaspace/src/components/modules/WorkshoppingPage/IdeaOverview/IdeaOverview.js index f40ddb297..556dfca72 100644 --- a/apps/ideaspace/src/components/modules/WorkshoppingPage/IdeaOverview/IdeaOverview.js +++ b/apps/ideaspace/src/components/modules/WorkshoppingPage/IdeaOverview/IdeaOverview.js @@ -1,40 +1,41 @@ +import { atoms } from '@devlaunchers/components/src/components'; import { IdeaCard } from "./IdeaCard/IdeaCard"; -import { DescriptionCard } from "./DescriptionCard/DescriptionCard"; import { IdeaOwnerCard } from "./IdeaOwnerCard/IdeaOwnerCard"; import { TagsCard } from "./TagsCard/TagsCard"; -import { IdeaFeaturesCard } from "./IdeaFeaturesCard/IdeaFeaturesCard"; -import { TargetAudieneCard } from "./TargetAudienceCard/TargetAudienceCard"; +import IdeaContentCard from "./IdeaContentCard/IdeaContentCard"; +import IdeaSettingsCard from "./IdeaSettingsCard/IdeaSettingsCard"; import { Wrapper, TopView, - BottomView, RightWrapper, LeftWrapper, } from './StyledComponents'; export const IdeaOverview = ({ selectedCard }) => { + if (selectedCard.ideaName === "") return null; + const authorName = selectedCard.ideaOwner?.userName !== undefined + ? selectedCard.ideaOwner?.userName + : selectedCard.ideaOwner?.email; + return ( - - - + - - + + - - - - + + + ); diff --git a/apps/ideaspace/src/components/modules/WorkshoppingPage/IdeaOverview/IdeaOwnerCard/StyledComponent.js b/apps/ideaspace/src/components/modules/WorkshoppingPage/IdeaOverview/IdeaOwnerCard/StyledComponent.js index 643f471a7..25da52eed 100644 --- a/apps/ideaspace/src/components/modules/WorkshoppingPage/IdeaOverview/IdeaOwnerCard/StyledComponent.js +++ b/apps/ideaspace/src/components/modules/WorkshoppingPage/IdeaOverview/IdeaOwnerCard/StyledComponent.js @@ -11,7 +11,6 @@ export const StyledCard = styled(Card)` @media (max-width: 1280px) { margin-right: 0px; margin-bottom: 25px; - margin-top: 25px; } `; diff --git a/apps/ideaspace/src/components/modules/WorkshoppingPage/IdeaOverview/IdeaSettingsCard/IdeaSettingsCard.js b/apps/ideaspace/src/components/modules/WorkshoppingPage/IdeaOverview/IdeaSettingsCard/IdeaSettingsCard.js new file mode 100644 index 000000000..72fe00a45 --- /dev/null +++ b/apps/ideaspace/src/components/modules/WorkshoppingPage/IdeaOverview/IdeaSettingsCard/IdeaSettingsCard.js @@ -0,0 +1,211 @@ +import React, { useState } from 'react'; +import { useRouter } from 'next/router'; +import { useUserDataContext } from '@devlaunchers/components/context/UserDataContext'; +import { atoms } from '@devlaunchers/components/src/components'; +import IdeaSettingsContent from './IdeaSettingsContent'; +import useConfirm from '../../../../common/DialogBox/DialogBox'; +import theme from '@devlaunchers/components/styles/theme'; +import { agent } from '@devlaunchers/utility'; +import { cleanData, cleanIdeaForPost } from '../../../../../utils/StrapiHelper'; + +const IdeaSettingsCard = ({ card }) => { + let { userData } = useUserDataContext(); + + const router = useRouter(); + const [cardStatus, setCardStatus] = useState(card.status); + const [disabling, setDisabling] = useState(false); + const [archiveButtonText, setArchiveButtonText] = useState('ARCHIVE'); + const [reactiveButtonText, setReactiveButtonText] = useState('REACTIVE'); + const [deleteButtonText, setDeleteButtonText] = useState('DELETE'); + + React.useEffect(() => { + if (card.status == 'archived') { + setDisabling(true); + } + }, []); + + const [ArchiveIdea, confirmArchive] = useConfirm( + ['Archive your idea', '', ''], + 'This action will pause your idea and you can reactivate it via your dashboard.', + ['primary alternative', 'archive', 'cancel'] + ); + + const [ArchiveFailure, confirmArchiveFailure] = useConfirm( + ['Unable to archive your idea', '', ''], + 'Please try again.', + ['primary', 'close'] + ); + + const [ArchiveSuccess, confirmArchiveSuccess] = useConfirm( + ['You idea was archived successfully', 'Success', ''], + 'Your idea is archived and it can be reactivated anytime via your dashboard.', + ['alternative primary', 'browse ideas', 'dashboard'] + ); + + const [ReactivateFailure, confirmReactivateFailure] = useConfirm( + ['Unable to reactivate your idea', '', ''], + 'Please try again.', + ['primary', 'close'] + ); + + const [DeleteIdea, confirmDelete] = useConfirm( + ['You are about to delete your Idea', 'Error', theme.colors.ERROR_500], + "This action can't be undone! Are you sure you want to proceed?", + ['primary alternative', 'delete', 'cancel'] + ); + + const [DeleteFailure, confirmDeleteFailure] = useConfirm( + ['Unable to delete your idea', '', ''], + 'Please try again.', + ['primary', 'close'] + ); + + const [DeleteSuccess, confirmDeleteSuccess] = useConfirm( + ['You idea was deleted successfully', 'Success', ''], + 'Your idea workshopping page is no longer accessible.', + ['alternative primary', 'browse ideas', 'submit new idea'] + ); + + const clickEdit = () => { + router.push(`/ideaspace/edit/${card.id}`); + }; + + const clickArchive = async () => { + setCardStatus(card['status']); + if (await confirmArchive()) { + card['status'] = 'archived'; + setArchiveButtonText(`WAIT`); + + try { + const res = putIdea(); + + if (res.status === 200) { + setArchiveButtonText(`ARCHIVE`); + setDisabling(true); + if (await confirmArchiveSuccess()) { + router.push(`/ideaspace/dashboard`); + } else { + router.push(`/ideaspace/browse`); + } + } + } catch (error) { + card['status'] = cardStatus; + confirmArchiveFailure(); + setArchiveButtonText(`ARCHIVE`); + } + } + }; + + const clickReactivate = async () => { + card['status'] = 'workshopping'; + setReactiveButtonText(`WAIT`); + + try { + const res = putIdea(); + + if (res.status === 200) { + setReactiveButtonText(`REACTIVE`); + setDisabling(false); + } + } catch (error) { + card['status'] = 'archived'; + confirmReactivateFailure(); + setReactiveButtonText(`REACTIVE`); + } + }; + + const clickDelete = async () => { + setCardStatus(card['status']); + if (await confirmDelete()) { + card['status'] = 'deleted'; + setDeleteButtonText(`WAIT`); + + try { + const res = putIdea(); + + if (res.status === 200) { + setDeleteButtonText(`DELETE`); + if (await confirmDeleteSuccess()) { + router.push(`/ideaspace/submit`); + } else { + router.push(`/ideaspace/browse`); + } + } + } catch (error) { + card['status'] = cardStatus; + confirmDeleteFailure(); + setDeleteButtonText(`DELETE`); + } + } + }; + + const putIdea = async () => { + const id = card.id; + const cardForPut = cleanIdeaForPost(card); + return cleanData(await agent.Ideas.put(id, cardForPut)); + }; + + if (card.author?.id !== userData.id) { + return null; + } + + return ( + + +

      Idea settings

      +
      + + +
      + {card.status == 'archived' ? ( + + ) : ( + + )} + +
      + + + + + + + + + + + +
      + ); +}; + +export default IdeaSettingsCard; diff --git a/apps/ideaspace/src/components/modules/WorkshoppingPage/IdeaOverview/IdeaSettingsCard/IdeaSettingsContent.js b/apps/ideaspace/src/components/modules/WorkshoppingPage/IdeaOverview/IdeaSettingsCard/IdeaSettingsContent.js new file mode 100644 index 000000000..6afcf6568 --- /dev/null +++ b/apps/ideaspace/src/components/modules/WorkshoppingPage/IdeaOverview/IdeaSettingsCard/IdeaSettingsContent.js @@ -0,0 +1,49 @@ +import React, { useState } from 'react'; +import axios from "axios"; +import theme from '@devlaunchers/components/styles/theme'; +import { atoms } from '@devlaunchers/components/src/components'; + +const IdeaSettingsContent = ({ + settingLabel, + settingText, + buttonText, + buttonFunction, + disabling, + buttonColor +}) => { + let [over,setOver]=React.useState(false); + let [bColor,setBColor]=React.useState(buttonColor); + + React.useEffect(() => { + if (buttonColor != undefined){ + if (over) { + setBColor(theme.colors.ERROR_700); + } else { + setBColor(buttonColor); + } + } + }, [over]); + + return ( + + + + {settingLabel} + + + {settingText} + + + + setOver(true)} onMouseOut={()=>setOver(false)} + > + {buttonText} + + + + ); +}; + +export default IdeaSettingsContent; \ No newline at end of file diff --git a/apps/ideaspace/src/components/modules/WorkshoppingPage/IdeaOverview/StyledComponents.js b/apps/ideaspace/src/components/modules/WorkshoppingPage/IdeaOverview/StyledComponents.js index 9178df433..0e5c44a75 100644 --- a/apps/ideaspace/src/components/modules/WorkshoppingPage/IdeaOverview/StyledComponents.js +++ b/apps/ideaspace/src/components/modules/WorkshoppingPage/IdeaOverview/StyledComponents.js @@ -41,16 +41,8 @@ export const TopView = styled.div` display: flex; height: 100%; width: 100%; - margin-bottom: 25px; + margin-bottom: 3rem; @media (max-width: 1280px) { flex-direction: column; } `; - -export const BottomView = styled.div` - display: flex; - flex-direction: column; - height: 100%; - width: 100%; - margin-top: 25px; -`; diff --git a/apps/ideaspace/src/components/modules/WorkshoppingPage/IdeaOverview/TagsCard/Tag.js b/apps/ideaspace/src/components/modules/WorkshoppingPage/IdeaOverview/TagsCard/Tag.js index 785e5e5ad..15c7eb04b 100644 --- a/apps/ideaspace/src/components/modules/WorkshoppingPage/IdeaOverview/TagsCard/Tag.js +++ b/apps/ideaspace/src/components/modules/WorkshoppingPage/IdeaOverview/TagsCard/Tag.js @@ -24,4 +24,4 @@ export const Tag = ({ tag }) => { ) -} \ No newline at end of file +} diff --git a/apps/ideaspace/src/components/modules/WorkshoppingPage/IdeaOverview/TagsCard/TagsCard.js b/apps/ideaspace/src/components/modules/WorkshoppingPage/IdeaOverview/TagsCard/TagsCard.js index 4e6fd6907..9cf3a1b71 100644 --- a/apps/ideaspace/src/components/modules/WorkshoppingPage/IdeaOverview/TagsCard/TagsCard.js +++ b/apps/ideaspace/src/components/modules/WorkshoppingPage/IdeaOverview/TagsCard/TagsCard.js @@ -13,7 +13,7 @@ export const TagsCard = ({ status }) => { Tags - + ); diff --git a/apps/ideaspace/src/components/modules/WorkshoppingPage/IdeaOverview/TargetAudienceCard/StyledComponents.js b/apps/ideaspace/src/components/modules/WorkshoppingPage/IdeaOverview/TargetAudienceCard/StyledComponents.js deleted file mode 100644 index 752a6f430..000000000 --- a/apps/ideaspace/src/components/modules/WorkshoppingPage/IdeaOverview/TargetAudienceCard/StyledComponents.js +++ /dev/null @@ -1,31 +0,0 @@ -import { Card } from '../../../../common/Card/Card' -import styled from '@emotion/styled'; - -export const StyledCard = styled(Card)` - display: flex; - flex-direction: column; - align-items: start; - justify-content: center; - padding: 20px; - padding-left: 30px; - padding-right: 30px; - margin-top: 25px; -`; - -export const Cardheading = styled.h3` - display: flex; - align-items: center; - justify-content: start; - margin-left: 8px; - margin-right: 8px; - margin-bottom: 20px; -`; - -export const TargetAudienceList = styled.div` - display: flex; - flex-direction: column; - align-items: start; - justify-content: center; - padding-bottom: 10px; - margin: 8px; -`; diff --git a/apps/ideaspace/src/components/modules/WorkshoppingPage/IdeaOverview/TargetAudienceCard/TargetAudienceCard.js b/apps/ideaspace/src/components/modules/WorkshoppingPage/IdeaOverview/TargetAudienceCard/TargetAudienceCard.js deleted file mode 100644 index db25dda60..000000000 --- a/apps/ideaspace/src/components/modules/WorkshoppingPage/IdeaOverview/TargetAudienceCard/TargetAudienceCard.js +++ /dev/null @@ -1,18 +0,0 @@ -import { - TargetAudienceList, - Cardheading, - StyledCard -} from './StyledComponents'; - -export const TargetAudieneCard = ({ targetAudience }) => { - return ( - - - Target Audience - - -
      {targetAudience}
      -
      -
      - ); -}; diff --git a/apps/ideaspace/src/components/modules/WorkshoppingPage/StyledComponents.js b/apps/ideaspace/src/components/modules/WorkshoppingPage/StyledComponents.js index 75760480c..ca20eec13 100644 --- a/apps/ideaspace/src/components/modules/WorkshoppingPage/StyledComponents.js +++ b/apps/ideaspace/src/components/modules/WorkshoppingPage/StyledComponents.js @@ -1,7 +1,9 @@ import styled from "styled-components"; export const Container = styled.div` - background-color: ${({ theme }) => theme.colors.NEUTRAL_2}; + background-color: ${({ theme }) => theme.colors.GREYSCALE_OFF_WHITE}; + display: flex; + flex-direction: column; box-sizing: border-box; padding: 25px; min-height: 100%; @@ -16,4 +18,40 @@ export const Wrapper = styled.div` width: 100%; align-items: center; justify-content: center; -`; \ No newline at end of file +`; + +export const ParaWrapper = styled.div` + display: flex; + margin: 40px; + align-items: center; + justify-content: center; +`; + +export const Headline = styled.div` + font-family: 'Abel'; + font-style: normal; + font-weight: 400; + font-size: 4rem; + line-height: 68px; + text-align: center; + letter-spacing: -0.02em; + color: #1C1C1C; +`; + +export const StyledRanbow = styled.div` + margin: 1rem auto 1rem auto; + max-width: 21rem; + height: 5px; +`; + +export const HeadWapper = styled.div` + background-color: #FFFFFF; + padding: 6rem 1rem 4rem 1rem; + @media (max-width: 1712px) { + padding: 3rem 1rem 2rem 1rem; + } + + @media (max-width: 529px) { + padding: 5rem 1rem 1rem 1rem; + } +`; diff --git a/apps/ideaspace/src/components/modules/WorkshoppingPage/WorkshoppingPage.js b/apps/ideaspace/src/components/modules/WorkshoppingPage/WorkshoppingPage.js index 8414b21ce..dcb59d512 100644 --- a/apps/ideaspace/src/components/modules/WorkshoppingPage/WorkshoppingPage.js +++ b/apps/ideaspace/src/components/modules/WorkshoppingPage/WorkshoppingPage.js @@ -1,52 +1,105 @@ import React, { useState } from 'react'; +import Error from "next/error"; import { useRouter } from 'next/router'; -import CommentList from './CommentsForm/DisplayComments'; +import DisplayComments from './CommentsForm/DisplayComments'; import CommentForm from './CommentsForm/CommentForm'; import { IdeaOverview } from './IdeaOverview/IdeaOverview'; +import { atoms } from '@devlaunchers/components/src/components'; +import BackButton from '../../common/BackButton/BackButton'; import theme from '../../../styles/theme'; import CircularIndeterminateLoader from '../Loader/CircularIndeterminateLoader' +import useConfirm from '../../common/DialogBox/DialogBox'; import { Form, Comments } from './CommentsForm/StyledComments'; + import { Container, Wrapper, + HeadWapper, + Headline, + StyledRanbow, + ParaWrapper, } from './StyledComponents'; import { useFetchIdea } from './useFetchIdea'; -function WorkshoppingPage() { +export default function WorkshoppingPage(props) { + const router = useRouter() - const { ideaId } = router.query; + + React.useEffect(() => { + if(!router.isReady){ return; } + }, [router.isReady]); + + const [comments, setComments] = useState([]); + const [handleChange, setHandleChange] = useState(''); - const [handleTextChange, setHandleTextChange] = useState(''); - const { data, loading } = useFetchIdea(ideaId) - return ( - - {loading === true ? - - : - - - -
      - - - - - {/* a count of the comments in the comment feed: */} - {/*
      Comment Feed: {data.comments.length}
      */} - -
      -
      - } -
      + + const { data, loading, hidden, getError } = useFetchIdea(router.query.ideaId, setComments); + + const [ArchivedIdea, confirmArchived] = useConfirm( + ["This Idea has been archived.", '', ''], + "You can't workshop on it.", + ['primary', 'got it', ''], ); -} -export default WorkshoppingPage; + React.useEffect(async () => { + if (hidden) { + await confirmArchived(); + window.history.back(-1); + } + }, [hidden]); + + function renderNewComment(comment) { + setComments([comment, ...comments]); + } + + if (getError) { + return ; + } else { + + return ( + + + + + Idea Workshop + + + + + + + Want to help this product idea? Comment and ideate this idea with other people to help + it become an open source project + + + + + {loading === true ? + + : + + + + +
      COMMENT FEED: {comments.length}
      + +
      + + + + +
      +
      + } +
      + ); + } +} \ No newline at end of file diff --git a/apps/ideaspace/src/components/modules/WorkshoppingPage/useFetchIdea.js b/apps/ideaspace/src/components/modules/WorkshoppingPage/useFetchIdea.js index 9f8a3dfc0..1825e2d4b 100644 --- a/apps/ideaspace/src/components/modules/WorkshoppingPage/useFetchIdea.js +++ b/apps/ideaspace/src/components/modules/WorkshoppingPage/useFetchIdea.js @@ -1,8 +1,13 @@ import { useState, useEffect } from 'react'; -import axios from 'axios'; +import { agent } from '@devlaunchers/utility'; +import { cleanData, cleanDataList } from '../../../utils/StrapiHelper'; +import { useUserDataContext } from '@devlaunchers/components/context/UserDataContext'; +export const useFetchIdea = (ideaId, setComments) => { + let { userData } = useUserDataContext(); -export const useFetchIdea = (ideaId) => { + const [hidden, setHidden] = useState(false); + const [getError, setGetError] = useState(false); const [loading, setLoading] = useState(false); const [data, setData] = useState({ ideaName: '', @@ -12,23 +17,61 @@ export const useFetchIdea = (ideaId) => { created_at: '', comments: [], author: {}, + difficultyLevel: '', + ideaOwner: '', }); - useEffect(() => { - const fetchIdea = async (ideaId) => { - setLoading(true) - const response = await axios.get(`${process.env.NEXT_PUBLIC_STRAPI_URL}/idea-cards/${ideaId}`); - setLoading(false) - if (response.data) { - setData(response.data) - } - } + const [sourceData, setSourceData] = useState({ + ideaName: '', + discord: '', + description: '', + email: '', + created_at: '', + comments: [], + author: {}, + }); + + // requests data from the backend + useEffect(async () => { try { if (ideaId) { - fetchIdea(ideaId) + setLoading(true); + + const data = cleanData(await agent.Ideas.getIdea(ideaId, new URLSearchParams(`populate=*`))); + + const commentResponse = data?.comments?.data; + if (commentResponse !== undefined) { + setComments(cleanDataList(commentResponse)) + } + + const author = data?.author?.data; + if (author !== undefined) { + data.author = cleanData(author); + } + + const ideaOwner = data?.ideaOwner?.data; + if (ideaOwner !== undefined) { + data.ideaOwner = cleanData(ideaOwner); + } + + setLoading(false); + + if (data) { + setSourceData(data); + } } - } catch(error) { + } catch (error) { console.log(error) + setGetError(true); } }, [ideaId, setLoading, setData]); - return { data, loading }; + + useEffect(() => { + if (sourceData.status == "archived" && sourceData.author.id !== userData.id) { + setHidden(true); + } else { + setData(sourceData); + } + }, [sourceData, userData]); + + return { data, loading, hidden, getError }; }; diff --git a/apps/ideaspace/src/pages/_app.js b/apps/ideaspace/src/pages/_app.js index 6e34f89db..437fb6959 100644 --- a/apps/ideaspace/src/pages/_app.js +++ b/apps/ideaspace/src/pages/_app.js @@ -1,23 +1,15 @@ import React from 'react'; import { useRouter } from 'next/router'; import { ThemeProvider } from 'styled-components'; -import GlobalStyle from '../styles/globals'; +import GlobalStyle from '@devlaunchers/components/src/styles/global'; import Head from 'next/head'; - -import { UserDataProvider } from '@devlaunchers/components/context/UserDataContext'; import IdeasBetaFeedbackModal from '../components/modules/IdeasBetaFeedbackModal'; import 'react-toastify/dist/ReactToastify.css'; -import platformTheme from '@devlaunchers/components/styles/theme'; -import ideaspaceTheme from '../styles/theme'; +import theme from '@devlaunchers/components/styles/theme'; import useMockDataInDevelopment from "../utils/useMockData"; -/* -import { UserDataProvider } from "@contexts/UserDataContext"; -import Header from "../components/common/Header"; -import Footer from "../components/common/Footer" -*/ const hashRedirect = (router) => { // Strip out hash from url (if any) so we can transition from HashRouter to BrowserRouter @@ -26,19 +18,6 @@ const hashRedirect = (router) => { } }; -const theme = () =>{ - // platformTheme and ideaspaceTheme both have color argument - // it need to concat instead of replace each other - for(let a in platformTheme){ - if(a in ideaspaceTheme){ - ideaspaceTheme[a] = {...platformTheme[a], ...ideaspaceTheme[a]}; - }else{ - ideaspaceTheme[a] = platformTheme[a]; - } - } - return ideaspaceTheme; -} - function MyApp(props) { //useMockDataInDevelopment(); @@ -51,16 +30,12 @@ function MyApp(props) { }, []); return ( -
      - - - - - - {props.children} - - -
      + + + + + {props.children} + ); } diff --git a/apps/ideaspace/src/pages/browse.js b/apps/ideaspace/src/pages/browse.js index 23da9c1dc..3111118b0 100644 --- a/apps/ideaspace/src/pages/browse.js +++ b/apps/ideaspace/src/pages/browse.js @@ -1,3 +1,13 @@ +import Head from 'next/head'; import BrowseIdeas from "../components/modules/BrowseIdeas/BrowseIdeas" -export default BrowseIdeas; +export default function BrowseIdeasRoute() { + return ( + <> + + Browse Ideas - Dev Launchers + + + + ); +}; \ No newline at end of file diff --git a/apps/ideaspace/src/pages/dashboard.js b/apps/ideaspace/src/pages/dashboard.js index 881b574bd..05a0de3d6 100644 --- a/apps/ideaspace/src/pages/dashboard.js +++ b/apps/ideaspace/src/pages/dashboard.js @@ -1,3 +1,13 @@ +import Head from 'next/head'; import DashboardPage from "../components/modules/DashboardPage/DashboardPage" -export default DashboardPage; \ No newline at end of file +export default function DashboardPageRoute() { + return ( + <> + + IdeaSpace Dashboard - Dev Launchers + + + + ); +}; \ No newline at end of file diff --git a/apps/ideaspace/src/pages/edit/[ideaId].js b/apps/ideaspace/src/pages/edit/[ideaId].js index 80b1aa5f8..6d30d679d 100644 --- a/apps/ideaspace/src/pages/edit/[ideaId].js +++ b/apps/ideaspace/src/pages/edit/[ideaId].js @@ -1,8 +1,13 @@ -import SubmissionForm from '../../components/modules/EditIdea/EditIdea'; -import React, { useState } from 'react'; +import Head from 'next/head'; +import EditIdea from '../../components/modules/EditIdea/EditIdea'; export default function App() { return ( - + <> + + Edit an Idea - Dev Launchers + + + ); -} +}; diff --git a/apps/ideaspace/src/pages/index.js b/apps/ideaspace/src/pages/index.js index 69311757e..4f8e39be3 100644 --- a/apps/ideaspace/src/pages/index.js +++ b/apps/ideaspace/src/pages/index.js @@ -1,3 +1,13 @@ +import Head from 'next/head'; import WelcomePage from "../components/modules/WelcomePage/WelcomePage" -export default WelcomePage; \ No newline at end of file +export default function WelcomePageRoute() { + return ( + <> + + IdeaSpace - Dev Launchers + + + + ); +}; \ No newline at end of file diff --git a/apps/ideaspace/src/pages/submit.js b/apps/ideaspace/src/pages/submit.js index 89c49595e..2e0aee035 100644 --- a/apps/ideaspace/src/pages/submit.js +++ b/apps/ideaspace/src/pages/submit.js @@ -1,3 +1,13 @@ +import Head from 'next/head'; import SubmissionForm from '../components/modules/SubmissionForm/SubmissionForm'; -export default SubmissionForm; +export default function SubmissionFormRoute() { + return ( + <> + + Submit an Idea - Dev Launchers + + + + ); +}; \ No newline at end of file diff --git a/apps/ideaspace/src/pages/workshop/[ideaId].js b/apps/ideaspace/src/pages/workshop/[ideaId].js index eb1ea90aa..0b9852e0c 100644 --- a/apps/ideaspace/src/pages/workshop/[ideaId].js +++ b/apps/ideaspace/src/pages/workshop/[ideaId].js @@ -1,8 +1,15 @@ +import Head from 'next/head'; import WorkshoppingPage from '../../components/modules/WorkshoppingPage/WorkshoppingPage'; import React from 'react'; +import { useRouter } from "next/router"; -export default function App() { +export default function WorkshopIdeaPage() { return ( - + <> + + Idea Workshop - Dev Launchers + + + ); -} +}; diff --git a/apps/ideaspace/src/utils/EnvironmentVariables.js b/apps/ideaspace/src/utils/EnvironmentVariables.js deleted file mode 100644 index 5ff0ce04c..000000000 --- a/apps/ideaspace/src/utils/EnvironmentVariables.js +++ /dev/null @@ -1,32 +0,0 @@ -const PROD = { - envType: "PROD", - API_URL: "https://api.devlaunchers.org", - STRAPI_URL: "https://api.devlaunchers.org", - GOOGLE_AUTH_URL: "https://api.devlaunchers.org/connect/google", - DISCORD_AUTH_URL: - "https://discord.com/api/oauth2/authorize?client_id=709889509864636496&redirect_uri=https%3A%2F%2Fapi.devlaunchers.org%2Fusers%2Fauth%2Fdiscord%2Fcallback&response_type=code&scope=identify", -}; - -const STAGING = { - envType: "STAGING", - API_URL: "https://api-staging.devlaunchers.org", - STRAPI_URL: "https://api-staging.devlaunchers.org", - GOOGLE_AUTH_URL: "https://api-staging.devlaunchers.org/connect/google", - DISCORD_AUTH_URL: - "https://discord.com/api/oauth2/authorize?client_id=815294711983112194&redirect_uri=https%3A%2F%2Fapi-staging.devlaunchers.org%2Fusers%2Fauth%2Fdiscord%2Fcallback&response_type=code&scope=identify", -}; - - -export const env = () => { - if (typeof window !== "undefined") { - const currentUrl = window.location.href; - if ( - currentUrl.indexOf("staging") !== -1 || - currentUrl.indexOf("localhost") !== -1 - ) { - return STAGING; - - } - } - return PROD; -}; diff --git a/apps/ideaspace/src/utils/Logout.js b/apps/ideaspace/src/utils/Logout.js index 9a33f05c7..855b1929f 100644 --- a/apps/ideaspace/src/utils/Logout.js +++ b/apps/ideaspace/src/utils/Logout.js @@ -1,6 +1,5 @@ import Router from "next/router"; import axios from "axios"; -import { env } from "./EnvironmentVariables"; const Logout = () => { axios diff --git a/apps/ideaspace/src/utils/StrapiHelper.js b/apps/ideaspace/src/utils/StrapiHelper.js new file mode 100644 index 000000000..1dc3faae8 --- /dev/null +++ b/apps/ideaspace/src/utils/StrapiHelper.js @@ -0,0 +1,21 @@ +export function cleanData(data) { + return { id: data.id, ...data?.attributes }; +} + +export function cleanDataList(dataList) { + return dataList.map((data) => cleanData(data)); +} + +export function packageData(data) { + const result = { data }; + delete result.data.id; + return result; +} + +export function cleanIdeaForPost(card) { + let idea = packageData(card); + idea.data.author = idea.data.author.id; + idea.data.ideaOwner = idea.data.ideaOwner.id; + idea.data.comments = idea.data.comments.data.map((c) => c.id); + return idea; +} diff --git a/apps/ideaspace/src/utils/useMockData/mockAxios/mockIdeaAxios.js b/apps/ideaspace/src/utils/useMockData/mockAxios/mockIdeaAxios.js index 231cfba46..d403a5b89 100644 --- a/apps/ideaspace/src/utils/useMockData/mockAxios/mockIdeaAxios.js +++ b/apps/ideaspace/src/utils/useMockData/mockAxios/mockIdeaAxios.js @@ -20,5 +20,17 @@ export default function initMockIdeaCardData() { const result = mockIdeaData.filter((item) => item.id == cardId); return [200, result[0]]; }); + + mock + .onPut(/\/idea-cards\/\d+/).replyOnce(500) + .onPut(/\/idea-cards\/\d+/).reply(200); + + mock + .onPost(`${process.env.NEXT_PUBLIC_STRAPI_URL}/idea-cards/`).replyOnce(500) + .onPost(`${process.env.NEXT_PUBLIC_STRAPI_URL}/idea-cards/`).reply(200,{id:203}); + + mock + .onDelete(/\/idea-cards\/\d+/).replyOnce(500) + .onDelete(/\/idea-cards\/\d+/).reply(200); } diff --git a/apps/ideaspace/src/utils/useMockData/mockData/mockIdeaData.js b/apps/ideaspace/src/utils/useMockData/mockData/mockIdeaData.js index bf4818fc4..91701adc7 100644 --- a/apps/ideaspace/src/utils/useMockData/mockData/mockIdeaData.js +++ b/apps/ideaspace/src/utils/useMockData/mockData/mockIdeaData.js @@ -376,7 +376,8 @@ export let mockIdeaData = [ "targetAudience": "This is a multiline\nHELPFULNESS\nanswer", "hourCommitmentMin": 1, "hourCommitmentMax": 4, - "statu": "approved", + //"status": "approved", + "status": "archived", "author": { id: 2 }, @@ -389,7 +390,7 @@ export let mockIdeaData = [ "calendly": "no", "features": "This is a multiline\nFEATURES\nanswer", "experience": "This is a multiline\nEXPERIENCE\nanswer", - "involveLevel": "four", + "involveLevel": "highly", "skills": [ { "id": 251, @@ -480,6 +481,7 @@ export let mockIdeaData = [ "calendly": "No", "features": "", "experience": "", + "status": "archived", "skills": [ { "id": 114, diff --git a/apps/site-projects/package.json b/apps/site-projects/package.json index 786b1a827..563b11188 100644 --- a/apps/site-projects/package.json +++ b/apps/site-projects/package.json @@ -3,6 +3,7 @@ "version": "0.1.0", "private": true, "dependencies": { + "@devlaunchers/utility": "workspace:*", "@types/react-burger-menu": "2.8.3", "axios": "^0.27.2", "constate": "^3.3.2", @@ -52,10 +53,7 @@ "eslint-plugin-jsx-a11y": "^6.6.1", "eslint-plugin-react": "^7.30.1", "eslint-plugin-react-hooks": "^4.6.0", - "husky": "^8.0.1", - "lint-staged": "^13.0.3", "prettier": "^2.7.1", - "semantic-release": "^19.0.3", "typescript": "^4.7.4", "webp-loader": "0.6.0", "webpack": "^5.74.0" @@ -82,27 +80,7 @@ "not ie <= 11", "not op_mini all" ], - "lint-staged": { - "*.js": "eslint '*/**/*.{js,jsx}' --fix", - "*.{js,css,md}": "prettier --write" - }, "resolutions": { "styled-components": "^5" - }, - "release": { - "branches": [ - "release" - ], - "plugins": [ - "@semantic-release/commit-analyzer", - "@semantic-release/release-notes-generator", - [ - "@semantic-release/npm", - { - "npmPublish": false - } - ], - "@semantic-release/github" - ] } } diff --git a/apps/site-projects/src/components/common/Calendar/Calendar.js b/apps/site-projects/src/components/common/Calendar/Calendar.js deleted file mode 100644 index 61feab6f3..000000000 --- a/apps/site-projects/src/components/common/Calendar/Calendar.js +++ /dev/null @@ -1,96 +0,0 @@ -import React, { useState } from "react"; -import axios from "axios"; -import { DateTime } from "luxon"; -import { Wrapper, Event, Day, WeekdayTitle } from "./StyledCalendar"; - -const Calendar = ({ URL }) => { - const [eventList, setEventList] = useState([]); - - const current = DateTime.now(); - const max = current.plus({ days: 7 }); - - /* gets the JSON data from google calendar and makes a new array with only the properties we need */ - - const makeRequest = () => { - axios - .get(URL) - .then((response) => { - const tempEventList = []; - response.data.items.forEach((entry) => { - const time = DateTime.fromISO(entry.start.dateTime, { - zone: entry.start.timeZone, - }).setZone(); - tempEventList.push({ - name: entry.summary, - time: time.toFormat("t"), - weekday: time.weekday, - }); - }); - - setEventList(tempEventList); - }) - .catch(() => {}); - }; - - React.useEffect(makeRequest, []); - - const weekdays = ["MON", "TUES", "WED", "THURS", "FRI", "SAT", "SUN"]; - const dates = []; - - /* stores the dates of the previous weekdays in the Array, then today, then the remaining days in this week */ - const createDate = () => { - const todayInt = current.weekday; - for (let i = 1; i < todayInt; i++) { - dates.push(max.minus({ days: todayInt - i }).day); - } - dates.push(current.day); - - for (let i = todayInt + 1; i < 8; i++) { - dates.push(current.plus({ days: i - todayInt }).day); - } - - return dates; - }; - const dateslist = createDate(); - - return ( - - {weekdays.map((day, i) => { - const date = dateslist[i]; - return ( - - -
      {day}
      - -
      - {date} -
      -
      - {eventList.map(({ name, time, weekday }) => { - if (weekday === i + 1) { - return ( - - {name} -
      - {time} -
      - ); - } - return null; - })} - {eventList.filter(({ weekday }) => weekday === i + 1).length === - 0 ? ( -
      - No events -
      - ) : ( - "" - )} -
      - ); - })} -
      - ); -}; - -export default Calendar; diff --git a/apps/site-projects/src/components/common/Calendar/StyledCalendar.js b/apps/site-projects/src/components/common/Calendar/StyledCalendar.js deleted file mode 100644 index 6eade9ebb..000000000 --- a/apps/site-projects/src/components/common/Calendar/StyledCalendar.js +++ /dev/null @@ -1,57 +0,0 @@ -import Styled from "styled-components"; - -import theme from "../../../styles/theme"; - -export const Wrapper = Styled.div` - display: flex; - grid-area: WeeksGlance; - flex-direction: row; - width: 100%; -background-color: #DAD8D9; - - @media (orientation: portrait) { - flex-direction: column; - } -;`; - -export const Day = Styled.div` - display: flex; - color: #1c1c1c; - font-weight: 500; - padding-bottom: .5rem; - flex-direction: column; - font-size: 20px; - background-color: transparent; - width: 15%; - - @media (orientation: portrait) { - width:100%; - } -`; - -export const Event = Styled.div` - display: flex; - flex-wrap: wrap; - overflow: hidden; - white-space: nowrap; - background: bisque; - color: #f0edee; - width: 98%; - background-color: #ff7f0e; - margin-top: 1rem; - font-size: .9rem; - justify-content: center; - text-align: center; - padding-top: .3rem; - padding-bottom: .3rem; - font-family: roboto; -`; - -export const WeekdayTitle = Styled.div` - font-family: ${theme.fonts.headline}; - background-color: #f0edee; - color: #1c1c1c; - font-weight: 500; - padding-left: .5rem; - -`; diff --git a/apps/site-projects/src/components/common/Calendar/index.js b/apps/site-projects/src/components/common/Calendar/index.js deleted file mode 100644 index 6c64b5c40..000000000 --- a/apps/site-projects/src/components/common/Calendar/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from "./Calendar"; diff --git a/apps/site-projects/src/components/common/CardGroup/CardGroup.tsx b/apps/site-projects/src/components/common/CardGroup/CardGroup.tsx deleted file mode 100644 index 2a4cd8e73..000000000 --- a/apps/site-projects/src/components/common/CardGroup/CardGroup.tsx +++ /dev/null @@ -1,38 +0,0 @@ -// eslint-disable-next-line no-use-before-define -import React from "react"; -import Card from "../Card/Card"; -import { Collection, CollectionTitle } from "./StyledCardGroup"; -import { CardProps } from "../Card/Card" -interface PropTypes { - title?: String, - cards: CardProps[], - size: number, - attachment: string - titleAlignment: string, - cardGroupFlexDirection: string, -} - -export default function CardGroup(props: PropTypes) { - return ( -
      - {props.title ? ( - - {props.title} - - ) : ( - - )} - - {Object.keys(props.cards).length !== 0 && - props.cards.map((card, i) => ( - - ))} - -
      - ); -} diff --git a/apps/site-projects/src/components/common/CardGroup/index.js b/apps/site-projects/src/components/common/CardGroup/index.js deleted file mode 100644 index c2d65d0fd..000000000 --- a/apps/site-projects/src/components/common/CardGroup/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from "./CardGroup.tsx"; diff --git a/apps/site-projects/src/components/common/Dropdown/DropdownButton.js b/apps/site-projects/src/components/common/Dropdown/DropdownButton.js deleted file mode 100644 index 14ddc0a73..000000000 --- a/apps/site-projects/src/components/common/Dropdown/DropdownButton.js +++ /dev/null @@ -1,42 +0,0 @@ -import React, { useEffect, useState, useRef } from "react"; -import { Wrapper, Toggle, Rooms } from "./StyledDropdownButton"; - -const DropdownButton = ({ toggleBtnText, dropdownItems, className }) => { - const [menuOpen, setMenuOpen] = useState(false); - const node = useRef(); - - const handleClickOutside = (e) => { - if (node.current.contains(e.target)) return; - // outside click - setMenuOpen(false); - }; - - useEffect(() => { - if (menuOpen) { - document.addEventListener("mousedown", handleClickOutside); - } else { - document.removeEventListener("mousedown", handleClickOutside); - } - - return () => { - document.removeEventListener("mousedown", handleClickOutside); - }; - }, [menuOpen]); - - return ( - - setMenuOpen(!menuOpen)} - as="button" - fontSize="1.2rem" - > - {toggleBtnText} - - - <>{dropdownItems} - - - ); -}; - -export default DropdownButton; diff --git a/apps/site-projects/src/components/common/Dropdown/StyledDropdownButton.js b/apps/site-projects/src/components/common/Dropdown/StyledDropdownButton.js deleted file mode 100644 index cc34302ff..000000000 --- a/apps/site-projects/src/components/common/Dropdown/StyledDropdownButton.js +++ /dev/null @@ -1,38 +0,0 @@ -import styled from "styled-components"; -import Button from "../Button"; - -export const Wrapper = styled.div` - position: relative; - display: inline-block; -`; -export const Toggle = styled(Button)` - padding-left: 2rem; - padding-right: 2rem; -`; -export const Rooms = styled.div` - display: ${({ isOpen }) => (isOpen ? "block" : "none")}; - position: absolute; - right: 50%; - left: -50%; - background-color: #f1f1f1; - min-width: 160px; - overflow: auto; - box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.2); - border: 1px solid black; - z-index: 1; - - & > * { - color: black; - border: 1px solid black; - padding: 12px 16px; - text-decoration: none; - text-align: center; - display: block; - - &:hover, - &:focus { - background-color: #ddd; - color: #000; - } - } -`; diff --git a/apps/site-projects/src/components/common/Dropdown/index.js b/apps/site-projects/src/components/common/Dropdown/index.js deleted file mode 100644 index d06e95af1..000000000 --- a/apps/site-projects/src/components/common/Dropdown/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from "./DropdownButton"; diff --git a/apps/site-projects/src/components/common/Footer/Footer.js b/apps/site-projects/src/components/common/Footer/Footer.js deleted file mode 100644 index e0ad74977..000000000 --- a/apps/site-projects/src/components/common/Footer/Footer.js +++ /dev/null @@ -1,72 +0,0 @@ -import React from "react"; -import Link from "next/link"; - -import { - Wrapper, - FooterLogo, - FooterNav, - NavEntry, - SocialMediaContainer, - SocialMediaLink, - OrgInfoArea, -} from "./StyledFooter"; -import RandomQuote from "./RandomQuote"; -import Newsletter from "./Newsletter"; - -export default function Footer() { - return ( - - - - - - CREATE - - - - - LEARN - - - {/* } - - PLAY - - { */} - - - SUPPORT US - - - - - JOIN - - - - - - - - - - - - - - - - - - - - Terms of Service - - - | - - - Privacy Policy - {" "} - {"- | "} ©Dev Launchers, 2020. - - - ); -} diff --git a/apps/site-projects/src/components/common/Footer/Newsletter/Newsletter.js b/apps/site-projects/src/components/common/Footer/Newsletter/Newsletter.js deleted file mode 100644 index 91060b6e3..000000000 --- a/apps/site-projects/src/components/common/Footer/Newsletter/Newsletter.js +++ /dev/null @@ -1,240 +0,0 @@ -import React, { useState } from "react"; -import { withTheme } from "styled-components"; -import axios from "axios"; -import { - Row, - Col, - ThankYouMessage, - ErrorMessage, - Secret, -} from "./StyledNewsletter"; -import validateEmail from "../../../../utils/ValidateEmail"; -import { env } from "../../../../utils/EnvironmentVariables"; - -const NewsLetter = (props) => { - const [email, setEmail] = useState(""); - const [isCorrect, setIsCorrect] = useState(false); - const [check, setCheck] = useState(false); - const [secret, setSecret] = useState(false); - const [isEmailAlreadyUsed, setIsEmailAlreadyUsed] = useState(false); - - const EmailValidation = () => { - if (email === "password") { - setSecret(true); - } else if (!validateEmail(email)) { - setCheck(true); - } else { - axios - .post(`${env().STRAPI_URL}/newsletters`, { - email, - }) - .then(() => { - setIsCorrect(true); - }) - .catch(() => { - setIsEmailAlreadyUsed(true); - }); - } - }; - - const sendEmail = () => { - EmailValidation(email); - }; - - if (secret) { - return ( -
      -

      - →{" "} - - 😄 - {" "} - ← -

      -
      - ); - } - if (isCorrect) { - return ( -
      - -

      Thank You!

      -
      - -

      You successfully signed up to our newsletter!

      -
      -
      - ); - } - if (check) { - return ( -
      -

      - - ✉ - -   Sign up to our newsletter!  -

      - - - setEmail(e.target.value)} - value={email} - type="email" - placeholder="Enter your email here!" - /> - - - - - - Please enter a valid email! -
      - ); - } - if (isEmailAlreadyUsed) { - return ( -
      -

      - - ✉ - -   Sign up to our newsletter!  -

      - - - setEmail(e.target.value)} - value={email} - type="email" - placeholder="Enter your email here!" - /> - - - - - - You already used this email before -
      - ); - } - return ( -
      -

      - - ✉ - -   Sign up to our newsletter!  -

      - - - setEmail(e.target.value)} - value={email} - type="email" - placeholder="Enter your email here!" - /> - - - - - -
      - ); -}; - -export default withTheme(NewsLetter); diff --git a/apps/site-projects/src/components/common/Footer/Newsletter/StyledNewsletter.js b/apps/site-projects/src/components/common/Footer/Newsletter/StyledNewsletter.js deleted file mode 100644 index 32af99dfa..000000000 --- a/apps/site-projects/src/components/common/Footer/Newsletter/StyledNewsletter.js +++ /dev/null @@ -1,156 +0,0 @@ -import styled, { keyframes } from "styled-components"; - -const fadeIn = keyframes` - from { - opacity: 0; - } - to { - opacity: 1; - } -`; - -export const Row = styled.div` - display: flex; -`; - -export const Col = styled.div` - font-family: "Nunito Sans"; - flex: ${props => props.size}; - color: ${props => props.theme.colors.NEUTRAL_1}; - background-color: ${props => props.theme.colors.ACCENT_4}; - transition: 0.5s; - border: 5px solid ${props => props.theme.colors.NEUTRAL_1}; - margin: 0 auto; - padding: 10px; - text-align: center; - margin-bottom: 30px; - - #userEmail { - border: 0; - background: transparent; - outline: none; - font-weight: bold; - width: 96%; - text-align: center; - border-right: none; - box-shadow: 0px 0px 0px -7px ${props => props.theme.colors.NEUTRAL_1}, - 0px 0px 0px -7px ${props => props.theme.colors.NEUTRAL_1}; - -webkit-transition: box-shadow 0.5s; - transition: box-shadow 0.5s ease-in-out; - } - - #userEmail::placeholder { - color: ${props => props.theme.colors.NEUTRAL_1}; - transition: opacity 1s; - opacity: 1; - } - - #userEmail:hover::placeholder { - color: ${props => props.theme.colors.NEUTRAL_1}; - opacity: 0.5; - } - - #userEmail:focus::placeholder { - color: ${props => props.theme.colors.NEUTRAL_1}; - opacity: 0.5; - } - - #userEmail:hover { - box-shadow: 0px 10px 0px -7px ${props => props.theme.colors.NEUTRAL_1}, - 0px 10px 0px -7px ${props => props.theme.colors.NEUTRAL_1}; - } - - #userEmail:focus { - box-shadow: 0px 10px 0px -7px ${props => props.theme.colors.NEUTRAL_1}, - 0px 10px 0px -7px ${props => props.theme.colors.NEUTRAL_1}; - } - - #submitButton { - background: transparent; - transition: 0.3s; - text-decoration: none; - text-align: center; - border: none; - outline: none; - font-weight: bolder; - height: 1.75rem; - width: 100%; - box-sizing: border-box; - display: block; - cursor: pointer; - letter-spacing: 0.025em; - } - - :hover #submitButton { - text-shadow: black 0px 0px 1px; - color: ${props => props.theme.colors.ACCENT_4}; - } - - :hover { - background-color: ${props => props.theme.colors.ACCENT_3}; - } -`; - -export const ThankYouMessage = styled.div` - font-family: "Nunito Sans"; - margin-top: 30px; - text-align: center; - animation: float 3s infinite; - - @keyframes float { - 0% { - transform: translateY(0px); - } - 50% { - transform: translateY(-20px); - } - 100% { - transform: translateY(0px); - } - } - - #thankYou { - display: inline; - font-size: 3.5rem; - font-weight: bolder; - background: linear-gradient( - to right, - hsl(28, 100%, 53%), - hsl(45, 100%, 53%), - hsl(194, 52%, 67%), - hsl(203, 48%, 44%) - ); - -webkit-background-clip: text; - -webkit-text-fill-color: transparent; - } - - #message { - margin-top: -20px; - font-size: 1.75rem; - font-weight: bolder; - color: ${props => props.theme.colors.NEUTRAL_2}; - } -`; - -export const ErrorMessage = styled.p` - color: red; - font-family: "Nunito Sans"; - font-weight: bolder; - font-size: 1.25rem; - margin-top: -20px; - margin-bottom: 20px; - animation: ${fadeIn} 0.5s linear; -`; - -export const Secret = styled.a` - font-size: 7rem; - text-align: center; - - :hover span { - display: none; - } - - :hover:before { - content: "🤫"; - } -`; diff --git a/apps/site-projects/src/components/common/Footer/Newsletter/index.js b/apps/site-projects/src/components/common/Footer/Newsletter/index.js deleted file mode 100644 index d34ac646c..000000000 --- a/apps/site-projects/src/components/common/Footer/Newsletter/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from "./Newsletter"; diff --git a/apps/site-projects/src/components/common/Footer/RandomQuote/RandomQuote.js b/apps/site-projects/src/components/common/Footer/RandomQuote/RandomQuote.js deleted file mode 100644 index f1dc2f081..000000000 --- a/apps/site-projects/src/components/common/Footer/RandomQuote/RandomQuote.js +++ /dev/null @@ -1,15 +0,0 @@ -import React from "react"; -import Wrapper from "./StyledRandomQuote"; - -// Retrieve all quotes from our content layer -const quotes = require("../../../../content/collections/quotes.json").data; - -export default function RandomQuote() { - // Select and display a random quote from our quotes collection - const randomQuote = quotes[parseInt(quotes.length * Math.random(), 10)]; - return ( - - "{randomQuote.body}" - {randomQuote.author} - - ); -} diff --git a/apps/site-projects/src/components/common/Footer/RandomQuote/StyledRandomQuote.js b/apps/site-projects/src/components/common/Footer/RandomQuote/StyledRandomQuote.js deleted file mode 100644 index 5289deab6..000000000 --- a/apps/site-projects/src/components/common/Footer/RandomQuote/StyledRandomQuote.js +++ /dev/null @@ -1,14 +0,0 @@ -import styled from "styled-components"; - -const Wrapper = styled.div` - text-align: center; - width: 60%; - padding-top: 0.5rem; - margin-top: -1.5%; - margin-bottom: -2%; - - @media (orientation: portrait) { - font-size: 3vw; - } -`; -export default Wrapper; diff --git a/apps/site-projects/src/components/common/Footer/RandomQuote/index.js b/apps/site-projects/src/components/common/Footer/RandomQuote/index.js deleted file mode 100644 index c8e325cdb..000000000 --- a/apps/site-projects/src/components/common/Footer/RandomQuote/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from "./RandomQuote"; diff --git a/apps/site-projects/src/components/common/Footer/StyledFooter.js b/apps/site-projects/src/components/common/Footer/StyledFooter.js deleted file mode 100644 index 6c9a79852..000000000 --- a/apps/site-projects/src/components/common/Footer/StyledFooter.js +++ /dev/null @@ -1,145 +0,0 @@ -import styled from "styled-components"; -import logoMonogramImage from "../../../images/logo-monogram.png?webp"; - -function getRandomThemeColor(theme) { - const colors = [ - theme.colors.ACCENT_1, - theme.colors.ACCENT_2, - theme.colors.ACCENT_3, - theme.colors.ACCENT_4, - ]; - let colorTrack = -1; - - return () => { - colorTrack = colorTrack >= colors.length - 1 ? 0 : colorTrack + 1; - return colors[colorTrack]; - }; -} - -export const Wrapper = styled.div` - position: relative; - background-color: #1c1c1c; - color: #d9d9d9; - width: 100%; - height: 600px; - min-height: 500px; - - display: flex; - flex-direction: column; - justify-content: space-around; - align-items: center; - - z-index: 3; - box-shadow: 0 3px 6px rgba(0, 0, 0, 0.16), 0 3px 6px rgba(0, 0, 0, 0.23); -`; - -export const FooterLogo = styled.img.attrs(() => ({ - src: logoMonogramImage, - alt: "logo", -}))` - width: 7rem; - margin-right: 10px; - margin-left: 10px; - margin-top: 10px; - margin-bottom: -10px; - transition: 0.3s; - - :hover { - transform: rotate(-60deg); - } -`; - -export const FooterNav = styled.div` - min-width: 450px; - width: 50%; - display: flex; - flex-direction: row; - justify-content: space-around; - align-items: center; - margin-top: -50px; - @media (orientation: portrait) { - min-width: 200px; - width: 90%; - } -`; -export const SocialMediaContainer = styled.div` - margin-top: -3%; - margin-bottom: -1.5%; - min-width: 10rem; - width: 40%; - display: flex; - flex-direction: row; - justify-content: space-around; - align-items: center; - font-size: 5rem; - @media (orientation: portrait) { - width: 100%; - } -`; - -export const SocialMediaLink = styled.a.attrs(({ Type }) => { - let href; - let className; - switch (Type) { - case "Instagram": - href = "https://www.instagram.com/devlaunchers/"; - className = "fab fa-instagram"; - break; - case "Linkedin": - href = "https://www.linkedin.com/company/devlaunchers/"; - className = "fab fa-linkedin"; - break; - case "Twitch": - href = "https://www.twitch.com/devlaunchers/"; - className = "fab fa-twitch"; - break; - case "Discord": - href = "https://www.discord.io/devlaunchers/"; - className = "fab fa-discord"; - break; - default: - href = null; - className = null; - break; - } - return { href, className }; -})` - font-size: 5rem; - color: ${({ theme }) => theme.colors.ACCENT_4}; - - transition: 0.3s; - :hover { - color: ${({ theme }) => theme.colors.NEUTRAL_2}; - transform: scale(1.25); - } -`; - -export const NavEntry = styled.div` - cursor: pointer; - font-size: 1.75rem; - border-bottom: 0.4rem solid transparent; - padding-top: 0.3rem; - padding-left: 0.5rem; - padding-right: 0.5rem; - transition: 0.5s; - - color: ${({ theme }) => theme.colors.NEUTRAL_2}; - - &:hover { - color: ${({ theme }) => theme.colors.ACCENT_4}; - border-bottom: 0.4rem solid ${({ theme }) => getRandomThemeColor(theme)}; - } - - @media (orientation: portrait) { - font-size: 1.2rem; - } -`; - -export const OrgInfoArea = styled.div` - width: 90%; - text-align: right; - font-size: 0.85rem; - display: flex; - flex-direction: row; - justify-content: flex-end; -`; diff --git a/apps/site-projects/src/components/common/Footer/index.js b/apps/site-projects/src/components/common/Footer/index.js deleted file mode 100644 index 3738288b0..000000000 --- a/apps/site-projects/src/components/common/Footer/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from "./Footer"; diff --git a/apps/site-projects/src/components/common/HamburgerMenu/HamburgerMenu.module.css b/apps/site-projects/src/components/common/HamburgerMenu/HamburgerMenu.module.css deleted file mode 100644 index 0136757f2..000000000 --- a/apps/site-projects/src/components/common/HamburgerMenu/HamburgerMenu.module.css +++ /dev/null @@ -1,74 +0,0 @@ -.burgerButton { - position: fixed; - min-height: 5vh; - min-width: 5vh; - right: 1.5vh; - top: 1.5vh; - - display: none; /* Only display on mobile */ -} - -.menuBody { - top: 0; -} -.overlay { - top: 0px; - right: 0px; -} -.burgerBar { - background: #d9d9d9; -} - -.crossClass { - background: #d9d9d9; -} - -.Logo { - margin-top: 20%; - width: 50%; -} -.Logo a { - display: inline-flex; - flex-direction: row; - flex-wrap: wrap; - align-items: center; -} -.LogoWrapper { - display: inline; - width: auto; -} -.LogoWrapper:hover { - filter: brightness(0) saturate(100%) invert(88%) sepia(10%) saturate(6102%) - hue-rotate(358deg) brightness(101%) contrast(103%); -} -.LogoImageHolder { - width: 100%; - margin: 2%; - margin-bottom: 0; -} -.LogoImage { - display: inline-block; - width: 100%; -} -.LogoWords { - position: absolute; - bottom: 5%; - text-align: center; - width: 100%; - font-family: "Abel", sans-serif; - font-size: 1.25rem; - margin-left: 0.25rem; - line-height: 140%; -} - -.navEntry { - text-align: center; - width: 100%; - margin-top: 10%; -} - -@media (orientation: portrait) { - .burgerButton { - display: block; - } -} diff --git a/apps/site-projects/src/components/common/HamburgerMenu/HamburgerMenu.tsx b/apps/site-projects/src/components/common/HamburgerMenu/HamburgerMenu.tsx deleted file mode 100644 index fbdab8faf..000000000 --- a/apps/site-projects/src/components/common/HamburgerMenu/HamburgerMenu.tsx +++ /dev/null @@ -1,138 +0,0 @@ -// eslint-disable-next-line no-use-before-define -import React from "react"; -import Link from "next/link"; -import Image from "next/image"; -import { slide as SlideHamburgerMenu } from "react-burger-menu"; -import style from "./HamburgerMenu.module.css"; -import logoMonogramImage from "../../../images/logo-monogram.png?webp"; -import Logout from "../../../utils/Logout"; -import { env } from "../../../utils/EnvironmentVariables"; -import { useUserDataContext } from "../../../context/UserDataContext"; - -const HamburgerMenu: React.FC = () => { - const { userData } = useUserDataContext(); - const [menuOpen, setMenuOpen] = React.useState(false); - - // Called when the open/close state of the menu changes (onStateChange callback) - const isMenuOpen = (state: { isOpen: boolean }) => { - setMenuOpen(state.isOpen); - }; - - // Called whenever a navigation item in the menu is clicked (closes menu) - function handleNavClick(): void { - setMenuOpen(false); - } - - return ( - -
      -
      -
      - - -
      - Logo -
      -
      - -
      -
      -
      -
      - - -
      CREATE
      -
      - - - -
      LEARN
      -
      - - -
      - {userData.id ? ( - <> - - -
      VISIT ACCOUNT PAGE
      -
      - - -
      LOG OUT
      -
      - - ) : ( - -
      SIGN IN
      -
      - )} -
      - - {/* } - -
      PLAY
      - - { */} - - -
      EARN
      -
      - - - -
      SUPPORT US
      -
      - - - -
      JOIN
      -
      - -
      - {/* } -
      - {authUser ? ( - - ) : ( - - )} -
      - { */} -
      - {/* }
      Dev Launchers
      */} -
      -
      - ); -}; - -export default HamburgerMenu; diff --git a/apps/site-projects/src/components/common/HamburgerMenu/index.js b/apps/site-projects/src/components/common/HamburgerMenu/index.js deleted file mode 100644 index 5bb09ee01..000000000 --- a/apps/site-projects/src/components/common/HamburgerMenu/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from "./HamburgerMenu.tsx"; diff --git a/apps/site-projects/src/components/common/Header/AccountDropdown/AccountDropdown.js b/apps/site-projects/src/components/common/Header/AccountDropdown/AccountDropdown.js deleted file mode 100644 index 8c1d1b641..000000000 --- a/apps/site-projects/src/components/common/Header/AccountDropdown/AccountDropdown.js +++ /dev/null @@ -1,38 +0,0 @@ -import React from "react"; -import Link from "next/link"; -import Logout from "../../../../utils/Logout"; -import { env } from "../../../../utils/EnvironmentVariables"; -import { - AccountMenuDropdownItem, - AccountMenuDropdownButton, - MenuButton, -} from "./StyledAccountDropdown"; - -export default function AccountDropdown(props) { - return ( -
      - {props.userData.id ? ( - - {" "} - - - Visit Account Page - - - - Logout{" "} - - - } - > - ) : ( - - Sign In{" "} - - )} -
      - ); -} diff --git a/apps/site-projects/src/components/common/Header/AccountDropdown/StyledAccountDropdown.js b/apps/site-projects/src/components/common/Header/AccountDropdown/StyledAccountDropdown.js deleted file mode 100644 index 8e08734cd..000000000 --- a/apps/site-projects/src/components/common/Header/AccountDropdown/StyledAccountDropdown.js +++ /dev/null @@ -1,31 +0,0 @@ -import styled from "styled-components"; -import DropdownButton from "../../Dropdown"; -import Button from "../../Button"; - -export const AccountMenuDropdownButton = styled(DropdownButton)` - @media (orientation: portrait) { - display: none; - } -`; - -export const MenuButton = styled(Button)` - @media (orientation: portrait) { - display: none; - } -`; - -export const AccountMenuDropdownItem = styled.a` - font-family: ${({ theme }) => theme.fonts.headline}, sans-serif; - background-color: #1c1c1c; - - width: ${({ width }) => width || ""}; - border: 0px; - - cursor: pointer; - font-size: ${({ fontSize }) => fontSize || "1.1rem"}; - margin-top: ${({ marginTop }) => marginTop || ""}; - padding: 0.5rem; - padding-left: 1rem; - padding-right: 1rem; - font-weight: bolder; -`; diff --git a/apps/site-projects/src/components/common/Header/AccountDropdown/index.js b/apps/site-projects/src/components/common/Header/AccountDropdown/index.js deleted file mode 100644 index ff7b7ab66..000000000 --- a/apps/site-projects/src/components/common/Header/AccountDropdown/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from "./AccountDropdown"; diff --git a/apps/site-projects/src/components/common/Header/Header.js b/apps/site-projects/src/components/common/Header/Header.js deleted file mode 100644 index 3be442b81..000000000 --- a/apps/site-projects/src/components/common/Header/Header.js +++ /dev/null @@ -1,68 +0,0 @@ -import React from "react"; -import Link from "next/link"; -import AccountDropdown from "./AccountDropdown"; - -import { - HeaderBlock, - Logo, - LogoWrapper, - LogoImageHolder, - LogoImage, - LogoWords, - HeaderNav, - NavEntry, -} from "./StyledHeader"; - -import { useUserDataContext } from "../../../context/UserDataContext"; -import HamburgerMenu from "../HamburgerMenu"; - -export default function Header() { - const { userData } = useUserDataContext(); - - return ( - - - - - - - - - Dev Launchers - - - - - - - - CREATE - - - - - LEARN - - - {/* } - - PLAY - - { */} - - - SUPPORT US - - - - - JOIN - - - -
      - - - - ); -} diff --git a/apps/site-projects/src/components/common/Header/StyledHeader.js b/apps/site-projects/src/components/common/Header/StyledHeader.js deleted file mode 100644 index 2870feaec..000000000 --- a/apps/site-projects/src/components/common/Header/StyledHeader.js +++ /dev/null @@ -1,121 +0,0 @@ -import styled from "styled-components"; -import logoMonogramImage from "../../../images/logo-monogram.png?webp"; - -export const HeaderBlock = styled.div` - background-color: #1c1c1c; - min-height: 7.5vh; - width: 100%; - position: sticky; - top: 0; - z-index: 2; - - display: flex; - justify-content: space-between; - align-items: center; - - /* box-shadow: 0 3px 6px rgba(0, 0, 0, 0.16), 0 3px 6px rgba(0, 0, 0, 0.23); */ - - a { - color: ${({ theme }) => theme.colors.NEUTRAL_2}; - } -`; -export const Logo = styled.div` - width: 30%; - - a { - display: inline-flex; - flex-direction: row; - flex-wrap: nowrap; - align-items: center; - } -`; - -export const LogoWrapper = styled.div` - display: inline; - width: auto; - transition: 1s; - - &:hover { - filter: brightness(0) saturate(100%) invert(88%) sepia(10%) saturate(6102%) - hue-rotate(358deg) brightness(101%) contrast(103%); - } -`; - -export const LogoImageHolder = styled.div` - width: 3rem; - margin: 2%; - margin-bottom: 0; - @media (orientation: portrait) { - height: auto; - width: 4rem; - margin-left: 1vw; - } -`; -export const LogoImage = styled.img.attrs(() => ({ - src: logoMonogramImage, - alt: "logo", -}))` - display: inline-block; - width: 100%; -`; -export const LogoWords = styled.div` - width: 15rem; - font-family: "Abel", sans-serif; - font-size: 1.75rem; - margin-left: 0.25rem; - line-height: 140%; - @media (orientation: portrait) { - display: none; - } -`; - -export const HeaderNav = styled.div` - display: flex; - flex-direction: row; - justify-content: space-between; - font-size: 1rem; - width: 40%; - min-width: 300px; - height: 7.5vh; - align-items: center; - transition: 0.5s; - - & > a { - display: flex; - height: 100%; - align-items: center; - } - - @media (orientation: portrait) { - display: none; - } -`; - -export const NavEntry = styled.div` - font-family: "Abel", sans-serif; - cursor: pointer; -`; - -export const SignInArea = styled.div` - width: 10%; - text-align: center; - display: flex; - justify-content: space-around; - align-items: center; - @media (orientation: portrait) { - display: none; - } -`; -export const UserProfilePic = styled.img.attrs(() => ({ - alt: "profile-pic", -}))` - max-height: 3rem; - min-height: 2rem; - height: 5vw; - cursor: pointer; -`; -export const HeaderPusher = styled.div` - position: "relative"; - width: "100%"; - min-height: "7.5vh"; -`; diff --git a/apps/site-projects/src/components/common/Header/index.js b/apps/site-projects/src/components/common/Header/index.js deleted file mode 100644 index 2764567d9..000000000 --- a/apps/site-projects/src/components/common/Header/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from "./Header"; diff --git a/apps/site-projects/src/components/common/Modal/Modal.tsx b/apps/site-projects/src/components/common/Modal/Modal.tsx deleted file mode 100644 index 4c2f25347..000000000 --- a/apps/site-projects/src/components/common/Modal/Modal.tsx +++ /dev/null @@ -1,44 +0,0 @@ -import React, { ReactElement } from "react"; -import ReactModal from "react-modal"; -// eslint-disable-next-line import/extensions -import { ModalContent, customModalStyles } from "./StyledModal"; - -ReactModal.setAppElement("#__next"); - -interface ModalProp { - modalContent: ReactElement | string; - modalIsOpen: boolean; - openModal: () => void; - closeModal: () => void; -} - -export default function Modal(props: ModalProp) { - const [modalContent] = React.useState(props.modalContent); - - // Modal functions - const [modalIsOpen, setModalIsOpen] = React.useState(props.modalIsOpen); - const openModal = () => { - setModalIsOpen(true); - }; - const closeModal = () => { - setModalIsOpen(false); - }; - const afterOpenModal = () => { }; - - return ( - - - {props.modalContent ? props.modalContent : modalContent} - - - ); -} - - diff --git a/apps/site-projects/src/components/common/Modal/index.js b/apps/site-projects/src/components/common/Modal/index.js deleted file mode 100644 index 09b91f72b..000000000 --- a/apps/site-projects/src/components/common/Modal/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from "./Modal"; diff --git a/apps/site-projects/src/components/common/Section/Section.tsx b/apps/site-projects/src/components/common/Section/Section.tsx deleted file mode 100644 index bd5716472..000000000 --- a/apps/site-projects/src/components/common/Section/Section.tsx +++ /dev/null @@ -1,26 +0,0 @@ -// eslint-disable-next-line no-use-before-define -import React from "react"; -import CardGroup from "../CardGroup"; -// eslint-disable-next-line import/extensions -import { Group, Wrapper } from "./StyledSection"; - -interface ISection { - title: string; - data?: object; -} -const Section: React.FC = (props) => ( - -

      {props.title}

      -
      - {Object.keys(props.data).map((groupTitle, i) => { - const group: string = props.data[groupTitle]; - return ( - - - - ); - })} -
      -
      -); -export default Section; diff --git a/apps/site-projects/src/components/common/Section/StyledSection.tsx b/apps/site-projects/src/components/common/Section/StyledSection.tsx deleted file mode 100644 index 9c3fca16b..000000000 --- a/apps/site-projects/src/components/common/Section/StyledSection.tsx +++ /dev/null @@ -1,16 +0,0 @@ -import styled from "styled-components"; - -export const Wrapper = styled.div` - border-left: 1px solid black; - margin-bottom: 10%; - - h2 { - margin-left: 2%; - margin-bottom: 0; - } -`; - -export const Group = styled.div` - border-left: 1px solid black; - margin-left: 2%; -`; diff --git a/apps/site-projects/src/components/common/Section/index.js b/apps/site-projects/src/components/common/Section/index.js deleted file mode 100644 index 7f1968c58..000000000 --- a/apps/site-projects/src/components/common/Section/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from "./Section.tsx"; diff --git a/apps/site-projects/src/components/common/SignUpForm/SignUpForm.js b/apps/site-projects/src/components/common/SignUpForm/SignUpForm.js index 5c129f4cd..b04d334b6 100644 --- a/apps/site-projects/src/components/common/SignUpForm/SignUpForm.js +++ b/apps/site-projects/src/components/common/SignUpForm/SignUpForm.js @@ -13,7 +13,6 @@ import FormEntry from "./FormEntry"; import InputField from "./InputField"; import SelectField from "./SelectField"; import TextAreaField from "./TextAreaField"; -import { env } from "../../../utils/EnvironmentVariables"; import InputAdder from "./InputAdder"; function validateEmail(email) { @@ -74,7 +73,7 @@ export default function SignUpForm(props) { // console.log(response); }); axios - .post(`${env().STRAPI_URL}/applicants`, { + .post(`${process.env.NEXT_PUBLIC_STRAPI_URL}/applicants`, { email, name, age, diff --git a/apps/site-projects/src/components/modules/Projects/Project/Description/DescriptionContent/DescriptionContent.js b/apps/site-projects/src/components/modules/Projects/Project/Description/DescriptionContent/DescriptionContent.js index fecd2f96d..35c742085 100644 --- a/apps/site-projects/src/components/modules/Projects/Project/Description/DescriptionContent/DescriptionContent.js +++ b/apps/site-projects/src/components/modules/Projects/Project/Description/DescriptionContent/DescriptionContent.js @@ -16,12 +16,12 @@ const DescriptionContent = ({ data }) => { {/* eslint-disable-next-line react/no-children-prop */} - {!!images.length && ( + {!!images[0]?.image?.data?.length && ( - {images.map((image) => ( - + {images[0].image.data.map(({attributes, id}) => ( + ))} diff --git a/apps/site-projects/src/components/modules/Projects/Project/EditorNotification/EditorNotification.js b/apps/site-projects/src/components/modules/Projects/Project/EditorNotification/EditorNotification.js new file mode 100644 index 000000000..26d2932fd --- /dev/null +++ b/apps/site-projects/src/components/modules/Projects/Project/EditorNotification/EditorNotification.js @@ -0,0 +1,60 @@ + +import React from "react"; +import { withTheme } from "styled-components"; + +import { ThemeProvider } from "styled-components"; +import theme from "@devlaunchers/components/styles/theme"; +import { ButtonsContainer, Button } from "./StyledEditorNotification"; +import { useUserDataContext } from '@devlaunchers/components/context/UserDataContext'; + +import Alert from '@devlaunchers/components/components/molecules/Alert'; + +function isUserAProjectLeader(user, project) { + for (let i=0; i { + + const user = useUserDataContext().userData; + + if (!hasEditingAccess(user, project)) { + return (<>); + } + + return ( +
      + + +

      It looks like you have editing permissions for this page!

      +

      + Head to the dashboard in Strapi to make changes to the content of this page. +
      + Be sure to check "Remember Me" when signing into the Strapi page or you will run into issues +

      + + + + +
      +
      +
      + ); +} + +export default withTheme(EditorNotification); diff --git a/apps/site-projects/src/components/modules/Projects/Project/EditorNotification/StyledEditorNotification.js b/apps/site-projects/src/components/modules/Projects/Project/EditorNotification/StyledEditorNotification.js new file mode 100644 index 000000000..e381873fd --- /dev/null +++ b/apps/site-projects/src/components/modules/Projects/Project/EditorNotification/StyledEditorNotification.js @@ -0,0 +1,60 @@ + +import styled from "styled-components"; + + +export const Wrapper = styled.div` + font-family: "Abel"; + align-content: center; + text-align: center; + display: grid; + row-gap: 1rem; + margin: auto; + padding-top: 1rem; + padding-bottom: 1rem; + h3 { + align-content: center; + text-align: center; + margin: auto; + font-size: 2rem; + } + p { + align-content: center; + text-align: center; + margin: auto; + } + @media (max-width: 52em) { + flex-direction: row; + } +`; + +export const ButtonsContainer = styled.div` + display: flex; + flex-direction: row; + column-gap: 2rem; + + margin: auto; + i { + font-size: 1.5rem; + } + + @media (max-width: 52em) { + flex-direction: row; + } +`; + +export const Button = styled.a` + font-size: 1.13rem; + display: flex; + justify-content: center; + margin: auto; + align-items: center; + gap: 0.5rem; + width: 12em; + padding: 0.5rem; + background-color: ${({ bgColor }) => + bgColor || "4A9D48"}; + color: #F0EDEE; + white-space: nowrap; + border-radius: 5px; + word-wrap: no; +`; diff --git a/apps/site-projects/src/components/modules/Projects/Project/EditorNotification/index.js b/apps/site-projects/src/components/modules/Projects/Project/EditorNotification/index.js new file mode 100644 index 000000000..553aa4793 --- /dev/null +++ b/apps/site-projects/src/components/modules/Projects/Project/EditorNotification/index.js @@ -0,0 +1,2 @@ + +export { default } from "./EditorNotification"; diff --git a/apps/site-projects/src/components/modules/Projects/Project/HelpBuild/HelpBuild.js b/apps/site-projects/src/components/modules/Projects/Project/HelpBuild/HelpBuild.js index 73e8b0bcc..7255ff4d5 100644 --- a/apps/site-projects/src/components/modules/Projects/Project/HelpBuild/HelpBuild.js +++ b/apps/site-projects/src/components/modules/Projects/Project/HelpBuild/HelpBuild.js @@ -10,7 +10,7 @@ const HelpBuild = () => ( Title="" Content={ -

      Help build this project!

      +

      Join our mission!

      We are looking for sponsors to fund the financial costs of different operations, with your help we can build a better product! @@ -19,7 +19,7 @@ const HelpBuild = () => (

      diff --git a/apps/site-projects/src/components/modules/Projects/Project/OpenPositions/OpenPositions.js b/apps/site-projects/src/components/modules/Projects/Project/OpenPositions/OpenPositions.js deleted file mode 100644 index 5e6bc55ab..000000000 --- a/apps/site-projects/src/components/modules/Projects/Project/OpenPositions/OpenPositions.js +++ /dev/null @@ -1,29 +0,0 @@ -import React from "react"; -import { withTheme } from "styled-components"; -import { - Wrapper, - OpenRolesContainer, - OpenRoleListing, -} from "./StyledOpenPositions"; - -const OpenPositions = (props) => { - const { projectData } = props; - - return ( - -

      - Open Positions -

      - - {projectData?.opportunities.map((element, i) => ( - -
      {element.title}
      -

      {element.description}

      -
      - ))} -
      -
      - ); -}; - -export default withTheme(OpenPositions); diff --git a/apps/site-projects/src/components/modules/Projects/Project/OpenPositions/StyledOpenPositions.js b/apps/site-projects/src/components/modules/Projects/Project/OpenPositions/StyledOpenPositions.js deleted file mode 100644 index f30f21945..000000000 --- a/apps/site-projects/src/components/modules/Projects/Project/OpenPositions/StyledOpenPositions.js +++ /dev/null @@ -1,17 +0,0 @@ -import styled from "styled-components"; - -export const Wrapper = styled.div``; - -export const OpenRolesContainer = styled.div` - display: flex; - flex-direction: row; - flex-wrap: wrap; -`; - -export const OpenRoleListing = styled.div` - width: 50%; - - @media (orientation: portrait) { - width: 100%; - } -`; diff --git a/apps/site-projects/src/components/modules/Projects/Project/OpenPositions/index.js b/apps/site-projects/src/components/modules/Projects/Project/OpenPositions/index.js deleted file mode 100644 index a30ae6b12..000000000 --- a/apps/site-projects/src/components/modules/Projects/Project/OpenPositions/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from "./OpenPositions"; diff --git a/apps/site-projects/src/components/modules/Projects/Project/Project.js b/apps/site-projects/src/components/modules/Projects/Project/Project.js index 8e0ec792a..5446d9cd6 100644 --- a/apps/site-projects/src/components/modules/Projects/Project/Project.js +++ b/apps/site-projects/src/components/modules/Projects/Project/Project.js @@ -12,15 +12,16 @@ import Team from "./Team"; // import SignUpButton from "./SignUpButton"; import HeroSection from "./HeroSection"; +import EditorNotification from "./EditorNotification"; import Tags from "./Tags"; import Vision from "./Vision"; -import Role from "./Role/Role"; import Description from "./Description/Description"; import Milestones from "./Milestones"; import JoinSupport from "./JoinSupport"; import HelpBuild from "./HelpBuild"; import Sessions from "./Sessions"; -import {useUserDataContext} from "../../../../context/UserDataContext" +import { useUserDataContext } from '@devlaunchers/components/context/UserDataContext'; +import Role from "./Role/Role"; const Project = ({ project, theme }) => { const router = useRouter(); @@ -39,27 +40,28 @@ const Project = ({ project, theme }) => { return (
      + - + excuteScroll(roleRef), scrollToDonate: () => excuteScroll(donateRef), }} /> - - - {} - + + + {} + { }; export default withTheme(Project); - -// const Project = (props) => { -// const projectsData = useProjectsDataContext([]); -// const [projectData, setProjectData] = React.useState({ -// heroImage: "", -// catchPhrase: "", -// keywords: [], -// projectReferenceURLs: [], -// openPositions: [], -// meetingTimes: [], -// meetingLinkURLs: [], -// team: { members: [], leaders: [] }, -// }); - -// React.useEffect(() => { -// if (!projectsData.length) return; -// setProjectData( -// projectsData.filter((entry) => entry.slug === props.projectId)[0] -// ); -// }, [projectsData, props.projectId]); - -// return ( -// -// -//
      -//
      -// {projectData?.vision} -//
      -//
      -//

      Description:

      {" "} -// {projectData?.description?.split("\n").map((text, i) => ( -//

      {text}

      -// ))} -//
      -// -//
      -// {/* } -// -// Project -// -// { */} -// -//

      Commitment Level

      -//

      {projectData?.commitmentLevel}

      - -//

      Project References

      -// {projectData?.projectReferenceURLs.map((element, i) => ( -//

      -// -// {element.title} -// -//

      -// ))} -//
      -//
      -//
      -// {/* } -// Commitment/Meetings -// { */} -// -//

      Meeting Times

      -// {projectData?.meetingTimes.map((meeting, i) => ( -//

      -// {meeting.title} {meeting.dateTime} -//

      -// ))} -//

      Meeting Links

      -// {projectData?.meetingLinkURLs.map((url) => ( -//

      -// -// {url.roomName} -// -//

      -// ))} -//
      -//
      -//
      -//
      -//
      -// {projectData.openPositions.length ? ( -//
      -// -//
      -// ) : ( -// "" -// )} -// {projectData.team.leaders.length || projectData.team.members.length ? ( -//
      -// -//
      -// ) : ( -// "" -// )} -//
      -//
      -// -// JOIN NOW -// -//
      -//
      -// -// {"<<"} Back to Projects -// -//
      -//
      -// ); -// }; - -// export default withTheme(Project); diff --git a/apps/site-projects/src/components/modules/Projects/Project/Team/Team.js b/apps/site-projects/src/components/modules/Projects/Project/Team/Team.js index e98480282..afc0f938a 100644 --- a/apps/site-projects/src/components/modules/Projects/Project/Team/Team.js +++ b/apps/site-projects/src/components/modules/Projects/Project/Team/Team.js @@ -10,12 +10,12 @@ const Team = ({ data }) => (

      Leaders

      - {data.leaders?.map((leader) => ( + {data?.leaders?.map((leader) => (
      (

      Members

      - {data.members?.map((member) => ( + {data?.members?.map((member) => (
      { } const items = searchValue ? searchResult : projects; - return (
      { alignItems: 'center', }} > -

      Projects you can join!

      +

      Our Projects - Explore and Collaborate!

      - Create, discover, and join an agile team building open-source software projects! We help members to contribute meaningfully and gain industry-ready experience along the way. Build epic products, tools, and apps used by real people while learning valuable skills and meeting awesome people! -
      + Come together to contribute, collaborate, and excel! Access a wealth of resources, tools, and support designed to help you succeed in building projects in the Dev Launchers ecosystem. Find a place you fit! +
      - {items.map((project, i) => ( + {items.map((project, i) => { + const imageUrl = process.env.NEXT_PUBLIC_NAME == "DEVELOPMENT" ? process.env.NEXT_PUBLIC_API_BASE_URL + project?.attributes?.heroImage?.data?.attributes?.url : project?.attributes?.heroImage?.data?.attributes?.url; + const attributes = project?.attributes; + return( interest), - description: project.catchPhrase, - href: project.slug, - imageSrc: project?.heroImage?.url, + id: attributes.id, + title: attributes.title, + secondaryText: `Commitment level: ${attributes.commitmentLevel}`, + tags: attributes?.interests?.data?.map(({ attributes }) => attributes.interest), + description: attributes.catchPhrase, + href: attributes.slug, + imageSrc: imageUrl, actions: ( <> - + LEARN MORE @@ -82,7 +84,8 @@ const Projects = ({ projects }) => { }} /> - ))} + ) +})}
      ); diff --git a/apps/site-projects/src/context/UserDataContext.js b/apps/site-projects/src/context/UserDataContext.js deleted file mode 100644 index 23da50e6d..000000000 --- a/apps/site-projects/src/context/UserDataContext.js +++ /dev/null @@ -1,63 +0,0 @@ -import React from "react"; -import constate from "constate"; // State Context Object Creator -import axios from "axios"; - -import { env } from "../utils/EnvironmentVariables"; - -const DEFAULT_USER = { - id: 0, - name: "", - username: "", - email: "", - bio: "", - profilePictureUrl: "", - socialMediaLinks: [], - totalPoints: 0, - totalSeasonPoints: 0, - availablePoints: 0, - volunteerHours: 0, - discord: { - id: 0, - avatar: "", - username: "", - discriminator: "", - }, -}; - -// Built from this article: https://www.sitepoint.com/replace-redux-react-hooks-context-api/ - -// Step 1: Create a custom hook that contains your state and actions -function useUserData() { - const [userData, setUserData] = React.useState(DEFAULT_USER); - - React.useEffect(() => { - // Setting timeout because of environment variable hack - axios(`${env().STRAPI_URL}/users/me`, { - withCredentials: true, - }) - .then(({ data: currentUser }) => { - setUserData({ - id: currentUser.id, - name: currentUser.profile.displayName, - username: currentUser.username, - email: currentUser.email, - bio: currentUser.profile.bio, - profilePictureUrl: currentUser.profile.profilePictureUrl, - socialMediaLinks: currentUser.profile.socialMediaLinks, - totalPoints: currentUser.point.totalPoints, - totalSeasonPoints: currentUser.point.totalSeasonPoints, - availablePoints: currentUser.point.availablePoints, - volunteerHours: currentUser.point.volunteerHours, - }); - }) - .catch(() => { - // setUserData({ id: "invalid" }); - }); - }, []); - - return { userData }; -} - -// Step 2: Declare your context state object to share the state with other components -const [UserDataProvider, useUserDataContext] = constate(useUserData); -export { UserDataProvider, useUserDataContext }; diff --git a/apps/site-projects/src/pages/[slug].jsx b/apps/site-projects/src/pages/[slug].jsx index c22f9c03a..889b0647a 100644 --- a/apps/site-projects/src/pages/[slug].jsx +++ b/apps/site-projects/src/pages/[slug].jsx @@ -1,24 +1,13 @@ -import axios from "axios"; import Head from "next/head"; import Project from "../components/modules/Projects/Project"; -import { env } from "../utils/EnvironmentVariables"; +import agent from "@devlaunchers/utility/agent" // const data = require("../components/modules/Projects/data.json"); export const getStaticPaths = async () => { - const { data } = await axios( - `${env().STRAPI_URL}/projects?_publicationState=live`, - { - headers: { - Accept: "application/json, text/plain, */*", - "User-Agent": - "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.107 Safari/537.36", - }, - } - ); - + const data = await agent.Projects.list({ _publicationState: 'live' }); const paths = data.map((project) => ({ - params: { slug: project.slug }, + params: { slug: project.attributes?.slug }, })); return { @@ -29,22 +18,7 @@ export const getStaticPaths = async () => { export const getStaticProps = async (context) => { const { slug } = context.params; - const { data: project } = await axios.get( - `${env().STRAPI_URL}/projects/${slug}`, - { - headers: { - Accept: "application/json, text/plain, */*", - "User-Agent": - "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.107 Safari/537.36", - }, - } - ); - - if (!project) { - return { - notFound: true, - }; - } + const project = await agent.Projects.get(slug, { "populate[heroImage][populate]": '*' }); return { props: { @@ -55,40 +29,41 @@ export const getStaticProps = async (context) => { }; const ProjectRoute = ({ project }) => { - const heroImageFormats = project?.heroImage?.formats; + const heroImageFormats = project?.attributes.heroImage?.attributes?.formats; const heroImage = heroImageFormats?.large || heroImageFormats?.medium || heroImageFormats?.small || - project?.heroImage?.url; + project?.attributes.heroImage?.attributes?.url; return ( + <> - {project?.title} - - + {project?.attributes?.title} + + - - - + + + - + - - + + diff --git a/apps/site-projects/src/pages/_app.jsx b/apps/site-projects/src/pages/_app.jsx index 2e964b9ac..205b7d815 100644 --- a/apps/site-projects/src/pages/_app.jsx +++ b/apps/site-projects/src/pages/_app.jsx @@ -1,29 +1,25 @@ -import { useRouter } from "next/router"; -import Script from "next/script"; -import { ToastContainer } from "react-toastify"; -import { ThemeProvider } from "styled-components"; -// import type { AppProps } from "next/app"; -import GlobalStyle from "../styles/globals"; +import { useRouter } from 'next/router'; +import Script from 'next/script'; +import { ToastContainer } from 'react-toastify'; +import { ThemeProvider } from 'styled-components'; +import GlobalStyle from '../styles/globals'; -import "react-toastify/dist/ReactToastify.css"; -import theme from "../styles/theme"; - -import { UserDataProvider } from "../context/UserDataContext"; +import 'react-toastify/dist/ReactToastify.css'; +import oldTheme from '../styles/theme'; const hashRedirect = (router) => { // Strip out hash from url (if any) so we can transition from HashRouter to BrowserRouter - if (router.asPath.startsWith("/#")) { - router.push(router.asPath.replace("/#", "")); + if (router.asPath.startsWith('/#')) { + router.push(router.asPath.replace('/#', '')); } }; -function MyApp(props) { +export default function MyApp(props) { const router = useRouter(); hashRedirect(router); - return ( <> - +
      + + +
      + +
      + {/* */} + + {props.children} + + {/* {props.children} */} +
      +
      + + + + ); +} + +export default MyApp; diff --git a/apps/user-profile/src/pages/_document.js b/apps/user-profile/src/pages/_document.js new file mode 100644 index 000000000..ce4aa84f0 --- /dev/null +++ b/apps/user-profile/src/pages/_document.js @@ -0,0 +1,53 @@ +import Document, { Html, Head, Main, NextScript } from "next/document"; + +import { ServerStyleSheet } from "styled-components"; + +export default class MyDocument extends Document { + render() { + return ( + + + + + + + +
      + + + + ); + } + + static async getInitialProps(ctx) { + const sheet = new ServerStyleSheet(); + const originalRenderPage = ctx.renderPage; + + try { + ctx.renderPage = () => + originalRenderPage({ + enhanceApp: (App) => (props) => + sheet.collectStyles(), + }); + + const initialProps = await Document.getInitialProps(ctx); + return { + ...initialProps, + styles: ( + <> + {initialProps.styles} + {sheet.getStyleElement()} + + ), + }; + } finally { + sheet.seal(); + } + } +} diff --git a/apps/user-profile/src/pages/onboarding.js b/apps/user-profile/src/pages/onboarding.js new file mode 100644 index 000000000..2513f7f8a --- /dev/null +++ b/apps/user-profile/src/pages/onboarding.js @@ -0,0 +1,15 @@ +import Head from 'next/head'; +import OnboardingLandingPage from '../components/modules/OnboardingLandingPage'; + +export default function UserOnboardingPage() { + return ( + <> + + Onboarding + +
      + +
      + + ); +} diff --git a/apps/user-profile/src/pages/users/[userId].js b/apps/user-profile/src/pages/users/[userId].js new file mode 100644 index 000000000..364c56ec6 --- /dev/null +++ b/apps/user-profile/src/pages/users/[userId].js @@ -0,0 +1,39 @@ +import React from 'react'; +import axios from 'axios'; + +import UserProfile from '../../components/modules/UserProfile'; + +export const getStaticPaths = async () => { + return { + paths: [], + fallback: 'blocking', + }; +}; + +export const getStaticProps = async (context) => { + const { userId } = context.params; + + // only numbers + const numberRegex = /^\d+$/; + + let { data: user } = userId.match(numberRegex) + ? await axios( + `${process.env.NEXT_PUBLIC_STRAPI_URL}/users/${userId}?populate=deep` + ) + : { data: null }; + + return !user + ? { notFound: true } + : { + props: { user: user }, + revalidate: 20, + }; +}; + +export default function UserProfilePage({ user }) { + return ( +
      + +
      + ); +} diff --git a/apps/user-profile/src/pages/users/me.js b/apps/user-profile/src/pages/users/me.js new file mode 100644 index 000000000..d9f26d285 --- /dev/null +++ b/apps/user-profile/src/pages/users/me.js @@ -0,0 +1,52 @@ +import React, { useEffect, useState } from 'react'; +import { useRouter } from 'next/router'; + +import { useUserDataContext } from '@devlaunchers/components/context/UserDataContext'; +import { featureFlags } from './../../utils/featureFlags'; + +import Head from 'next/head'; +import UserProfile from '../../components/modules/UserProfile'; +import UserOnboardingModal from '../../components/modules/UserOnboardingModal'; +import SignIn from '../../components/modules/UserProfile/SignIn'; +import PageBody from '../../components/common/PageBody'; + +/** + * @drescription This component renders the User Profile Component. + * A Modal is opened when user has not fully completed their onboarding. + */ +export default function UserProfilePage(props) { + const { isAuthenticated = false } = useUserDataContext(); + const router = useRouter(); + + /** + * @description Open modal when user is coming from the onbaording page. + * More conditions will be applied when modal should be opened in the future. + */ + const openUserOnboardingModal = () => { + if (featureFlags.inDevelopment || featureFlags.inStaging) { + return true; + } else { + // undo comment line 39, once backend fully integrated. + return false; // userData.hasOnboarded !== true; + } + }; + + return ( + <> + + User Profile + + + + {isAuthenticated ? ( + <> + {openUserOnboardingModal() && } + + + ) : ( + + )} + + + ); +} diff --git a/apps/user-profile/src/state/actions/index.js b/apps/user-profile/src/state/actions/index.js new file mode 100644 index 000000000..3983da25c --- /dev/null +++ b/apps/user-profile/src/state/actions/index.js @@ -0,0 +1 @@ +export * as onboardingActions from './onboardingActions' \ No newline at end of file diff --git a/apps/user-profile/src/state/actions/onboardingActions.js b/apps/user-profile/src/state/actions/onboardingActions.js new file mode 100644 index 000000000..e918b7566 --- /dev/null +++ b/apps/user-profile/src/state/actions/onboardingActions.js @@ -0,0 +1,21 @@ + +const SHOW_PLATFORM_ONBOARDING_MODAL = "Show platform onboarding modal."; +const HIDE_PLATFORM_ONBOARDING_MODAL = "Hide platform onbaording modal."; +const SHOW_INTRODUCTION_MODAL = "Show introduction modal."; +const HIDE_INTRODUCTION_MODAL = "Hide introduction modal."; +const HIDE_ALL_MODALS = "Hide All Modals"; +const SET_USERS_ROLE = "Set Users Role"; +const SET_USERS_EXPERIENCE = "Set Users Experience"; +const SET_USERS_INTEREST = "Set Users Interest"; +const REMOVE_USERS_INTEREST = "Remove Users Interest"; + +export { + SHOW_PLATFORM_ONBOARDING_MODAL, + HIDE_PLATFORM_ONBOARDING_MODAL, + SHOW_INTRODUCTION_MODAL, + HIDE_INTRODUCTION_MODAL, + HIDE_ALL_MODALS, + SET_USERS_ROLE, + SET_USERS_EXPERIENCE, + SET_USERS_INTEREST +} \ No newline at end of file diff --git a/apps/user-profile/src/state/reducers/index.js b/apps/user-profile/src/state/reducers/index.js new file mode 100644 index 000000000..e83822f45 --- /dev/null +++ b/apps/user-profile/src/state/reducers/index.js @@ -0,0 +1 @@ +export * as onboardingReducer from './onboardingReducer'; \ No newline at end of file diff --git a/apps/user-profile/src/state/reducers/onboardingReducer.js b/apps/user-profile/src/state/reducers/onboardingReducer.js new file mode 100644 index 000000000..f530ffcaf --- /dev/null +++ b/apps/user-profile/src/state/reducers/onboardingReducer.js @@ -0,0 +1,103 @@ + +import { onboardingActions } from './../actions'; +import { featureFlags } from './../../utils/featureFlags'; + +export const initialOnboardingState = { + showIntroductionModal: true, + showPlatformOnboardingModal: false, + user: { + selectedRole: null, + selectedExperience: null, + interest: [ + { id: 1, name: "Blockchain", selected: false }, + { id: 2, name: "Javascript", selected: false }, + { id: 3, name: "Web Design", selected: false }, + { id: 4, name: "UI/UX", selected: false }, + { id: 5, name: "React", selected: false }, + { id: 6, name: "QA", selected: false }, + { id: 7, name: "Web Development", selected: false }, + { id: 8, name: "C/C++/C#", selected: false }, + { id: 9, name: "Discord", selected: false }, + { id: 10, name: "Node.js", selected: false }, + { id: 11, name: "Strapi", selected: false }, + { id: 12, name: "Agile Development", selected: false }, + { id: 13, name: "HTML", selected: false }, + { id: 14, name: "Git", selected: false }, + { id: 15, name: "Frontend Development", selected: false }, + { id: 16, name: "Python", selected: false }, + { id: 17, name: "Data Structures", selected: false }, + { id: 18, name: "Prototyping", selected: false }, + { id: 19, name: "Project Management", selected: false } + + ] + }, +} + +export const onboardingReducer = (state, action) => { + switch (action.type) { + case onboardingActions.SET_USERS_INTEREST: { + console.log(action); + return { + ...state, + user: { + ...state.user, + interest: action.data, + } + } + } + case onboardingActions.SET_USERS_EXPERIENCE: { + return { + ...state, + user: { + ...state.user, + selectedExperience: action.data, + } + } + } + case onboardingActions.SET_USERS_ROLE: { + return { + ...state, + user: { + ...state.user, + selectedRole: action.data, + } + } + } + case onboardingActions.SHOW_PLATFORM_ONBOARDING_MODAL: { + return { + ...state, + showIntroductionModal: false, + showPlatformOnboardingModal: true + } + } + case onboardingActions.SHOW_INTRODUCTION_MODAL: { + return { + ...state, + showPlatformOnboardingModal: false, + showIntroductionModal: true + } + } + case onboardingActions.HIDE_PLATFORM_ONBOARDING_MODAL: { + return { + ...state, + showPlatformOnboardingModal: false + } + } + case onboardingActions.HIDE_INTRODUCTION_MODAL: { + return { + ...state, + showIntroductionModal: false + } + } + case onboardingActions.HIDE_ALL_MODALS: { + return { + ...state, + showIntroductionModal: false, + showPlatformOnboardingModal: false, + } + } + default: { + return state; + } + } +} \ No newline at end of file diff --git a/apps/user-profile/src/styles/globals.js b/apps/user-profile/src/styles/globals.js new file mode 100644 index 000000000..39437d3ec --- /dev/null +++ b/apps/user-profile/src/styles/globals.js @@ -0,0 +1,127 @@ +import { createGlobalStyle } from "styled-components"; +import { normalize } from "styled-normalize"; + +const GlobalStyle = createGlobalStyle` + ${normalize} + +html, +body { + padding: 0; + margin: 0; + /*font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, + Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;*/ + font-family: sans-serif; + font-family: "Nunito", sans-serif; + font-weight: lighter; + color: ${({ theme }) => theme.colors.GREYSCALE_OFF_BLACK}; + background-color: ${({ theme }) => theme.colors.GREYSCALE_OFF_WHITE}; +} + +html { + display: flex; + width: 100%; + min-height: 100%; + scroll-behavior: smooth; + /*overflow: auto;*/ +} + +body { + min-height: 100%; + margin: 0px; + width: 100%; + height: 100%; + font-size: 1.5rem; +} + +::-webkit-scrollbar { + width: 20px; +} + +::-webkit-scrollbar-thumb { + background: gray; + border-radius: 10px; + border-top: 5px solid transparent; + border-left: 5px solid transparent; + border-right: 5px solid transparent; + border-bottom: 5px solid transparent; + background-clip: content-box; +} + +#root { + overflow-x: hidden; +} + +.App { + font-family: sans-serif; + font-family: "Nunito Sans", sans-serif; + text-align: left; +} + +a { + width: auto; + display: inline; + color: ${({ theme }) => theme.colors.BLUE}; + text-decoration: none; + &:hover { + color: ${({ theme }) => theme.colors.LIGHT_BLUE}; + } +} +h1, +h2, +h3, +h4, +h5, +h6 { + margin-top: 1rem; + margin-bottom: 1rem; +} + +h1 { + font-family: ${({ theme }) => theme.fonts.headline}, sans-serif; + line-height: 1em; + margin-top: 4%; + margin-bottom: 4%; + + width: fit-content; + padding: 1rem; + // background-color:${({ theme }) => theme.colors.GREYSCALE_OFF_BLACK}; + // color:${({ theme }) => theme.colors.GREYSCALE_OFF_WHITE}; + + border-bottom: .3rem solid ${({ theme }) => theme.colors.GREYSCALE_OFF_BLACK}; +} + +h2 { + font-family: "Abel", sans-serif; +} + +h3 { + font-family: "Abel", sans-serif; + font-size: 1.9rem; + font-weight: bold; +} + +h4 { + font-family: "Abel", sans-serif; +} + +@media (orientation: portrait) { + html { + /* Scale all font down */ + font-size: 75%; + } + + .App { + } + + h1 { + font-size: 1.7rem; + } + + h2 { + font-size: 1.2rem; + } + +} + +`; +export default GlobalStyle; diff --git a/apps/user-profile/src/styles/theme.ts b/apps/user-profile/src/styles/theme.ts new file mode 100644 index 000000000..f40e9bec7 --- /dev/null +++ b/apps/user-profile/src/styles/theme.ts @@ -0,0 +1,63 @@ +const theme = { + colors: { + NEUTRAL_1: "#1c1c1c", // dark + ACCENT_1: "#ff7f0e", // orange + ACCENT_2: "#ffab00", // yellow + ACCENT_3: "#3a7ca5", // dark-blue + ACCENT_4: "#81c3d7", // loght-blue + ACCENT_5: "#ffc30f", // orange-yellow + ACCENT_6: "#C5B1A0", // beige + NEUTRAL_2: "#f0edee", // light + + NEUTRAL_3:"#59687B", + NEUTRAL_4:"#C4C4C4", + NEUTRAL_5:"#89969F", + NEUTRAL_6:"#C3C0C0", + // Dev Recruiters theme colors + White: '#FFFFFF', + Black: '#000000', + BlackT60: 'rgba(0,0,0,0.6)', + BlackT38: 'rgba(0,0,0,0.38)', + + AntiFlashWhite: '#F1F4F5', + AntiFlashWhiteT40: 'rgba(241,244,245,0.4)', + + SilverSand: '#C4C4C4', + SilverSandT20: 'rgba(196,196,196,0.2)', + + LightGray: '#D3D4D6', + LightGrayT90: 'rgba(211, 212, 214, 0.9)', + + Cultured: '#F8F8F8', + CulturedT87: 'rgba(248, 248, 248, 0.87)', + + Red: '#FF0000', + RedT31: 'rgba(255,0,0,0.31)', + + AppleT48: 'rgba(102, 176, 75, 0.48)', + + OuterSpace: '#454D58', + DarkElectricBlue: '#59687B', + Crayola: '#30363E', + Gray: '#7E8288', + CoolGrey: '#89969F', + Argent: '#C3C0C0', + BlackCoral: '#5B6068', + Platinum: '#E5E5E5', + SonicSilver: '#6F747C', + BrightGray: '#EDEDED', + DavysGrey: '#4F5154', + }, + fonts: { + headline: 'Abel', + normal: 'Nunito Sans', + }, + fontSizes: { + small: '1em', + medium: '2em', + large: '3em', + }, +} as const; +type Theme = typeof theme; +export type { Theme }; +export default theme; diff --git a/apps/website/src/utils/EnvironmentVariables.js b/apps/user-profile/src/utils/EnvironmentVariables.js similarity index 100% rename from apps/website/src/utils/EnvironmentVariables.js rename to apps/user-profile/src/utils/EnvironmentVariables.js diff --git a/apps/user-profile/src/utils/GoogleAnalytics.js b/apps/user-profile/src/utils/GoogleAnalytics.js new file mode 100644 index 000000000..438d7bf53 --- /dev/null +++ b/apps/user-profile/src/utils/GoogleAnalytics.js @@ -0,0 +1,10 @@ +import ReactGA from "react-ga"; + +export const initGA = () => { + ReactGA.initialize("UA-180113136-1"); +}; + +export const logPageView = () => { + ReactGA.set({ page: window.location.pathname }); + ReactGA.pageview(window.location.pathname); +}; diff --git a/apps/user-profile/src/utils/Logout.js b/apps/user-profile/src/utils/Logout.js new file mode 100644 index 000000000..9a33f05c7 --- /dev/null +++ b/apps/user-profile/src/utils/Logout.js @@ -0,0 +1,13 @@ +import Router from "next/router"; +import axios from "axios"; +import { env } from "./EnvironmentVariables"; + +const Logout = () => { + axios + .get(`${process.env.NEXT_PUBLIC_STRAPI_URL}/auth/logout`, { withCredentials: true }) + .then(() => { + Router.reload(window.location.pathname); + }); +}; + +export default Logout; diff --git a/apps/user-profile/src/utils/ScrollToTop.js b/apps/user-profile/src/utils/ScrollToTop.js new file mode 100644 index 000000000..bbf080b01 --- /dev/null +++ b/apps/user-profile/src/utils/ScrollToTop.js @@ -0,0 +1,16 @@ +import { useEffect } from "react"; + +function ScrollToTop({ history }) { + useEffect(() => { + const unlisten = history.listen(() => { + window.scrollTo(0, 0); + }); + return () => { + unlisten(); + }; + }, [history]); + + return null; +} + +export default ScrollToTop; diff --git a/apps/user-profile/src/utils/TimeZoneConverter.js b/apps/user-profile/src/utils/TimeZoneConverter.js new file mode 100644 index 000000000..a270b1ffd --- /dev/null +++ b/apps/user-profile/src/utils/TimeZoneConverter.js @@ -0,0 +1,31 @@ +import { DateTime } from "luxon"; + +export const ConvertCentralTime = (weekday = 6, hour, minute = 0) => + DateTime.fromObject( + { + weekday, + hour, + minute, + }, + { zone: "UTC-5" } + ).setZone("local"); + +export const ConvertCentralTimeString = ( + weekday = 0, + hour = 0, + minute = 0, + repeat = { weeks: 1 } +) => { + let centralDate = ConvertCentralTime(weekday, hour, minute); + if (DateTime.now() > centralDate) { + centralDate = centralDate.plus(repeat); + } + const localDate = centralDate.toLocaleString({ + weekday: "long", + month: "long", + day: "numeric", + hour: "numeric", + }); + const localTz = centralDate.toFormat("ZZZZ"); + return `${localDate} ${localTz}`; +}; diff --git a/apps/user-profile/src/utils/ValidateEmail.js b/apps/user-profile/src/utils/ValidateEmail.js new file mode 100644 index 000000000..507c75f65 --- /dev/null +++ b/apps/user-profile/src/utils/ValidateEmail.js @@ -0,0 +1,5 @@ +export default function validateEmail(email) { + const re = + /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; + return re.test(String(email).toLowerCase()); +} diff --git a/apps/user-profile/src/utils/featureFlags.js b/apps/user-profile/src/utils/featureFlags.js new file mode 100644 index 000000000..05a17f95a --- /dev/null +++ b/apps/user-profile/src/utils/featureFlags.js @@ -0,0 +1,14 @@ +const env = process.env.NEXT_PUBLIC_NAME?.toLowerCase(); + +const flags = { + useLiveData: { + always: true, + }, + env, + inDevelopment: env === 'development', + inStaging: env === 'staging', + inProduction: env === 'production', + bypassLogin: env === 'development' || env === 'staging' +} + +export const featureFlags = flags; \ No newline at end of file diff --git a/apps/user-profile/tsconfig.json b/apps/user-profile/tsconfig.json new file mode 100644 index 000000000..0b1a049c0 --- /dev/null +++ b/apps/user-profile/tsconfig.json @@ -0,0 +1,34 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "baseUrl": "./src", + "target": "esnext", + "lib": ["dom", "dom.iterable", "esnext"], + "module": "esnext", + "jsx": "preserve", + "incremental": true, + "paths": { + "@/lib/*": ["./lib/*"], + "@/components/*": ["./components/*"], + "@/pages/*": ["./pages/*"], + "@/public/*": ["../public/*"], + "@/themes/*": ["./themes/*"], + "@devlaunchers/site-projects/*": ["../../../apps/site-projects/src/pages/*"], + "@devlaunchers/dev-recruiters/*": ["../../../apps/dev-recruiters/src/pages/*"], + "@devlaunchers/components/*": ["../../../packages/UI/src/*"], + "@devlaunchers/components": ["../../../packages/UI/src/index"], + "@devlaunchers/core-lib/*": ["../../../packages/core-lib/src/*"], + "@devlaunchers/core-lib": ["../../../packages/core-lib/src/index"] + } + }, + "exclude": ["**/node_modules", "**/.*/", ".next"], + "include": [ + "next-env.d.ts", + "**/*.ts", + "**/*.tsx", + "**/*.js", + "**/*.jsx", + "**/*.mjs", + "**/*.json" +, ".eslintrc.js" ] +} \ No newline at end of file diff --git a/apps/user-profile/wrangler.toml b/apps/user-profile/wrangler.toml new file mode 100644 index 000000000..bb0dc2b7f --- /dev/null +++ b/apps/user-profile/wrangler.toml @@ -0,0 +1,15 @@ +type = "webpack" +account_id = "b0eb1f5510cb71344976cb9cac51e2af" +zone_id = "976b2978bfb8a506026238d437fe0555" + +[env.staging] +name = "website-staging" +route = "staging.devlaunchers.org/*" + +[env.production] +name = "website" +route = "devlaunchers.org/*" + +[site] +bucket = "./build" +entry-point = "workers-site" \ No newline at end of file diff --git a/apps/website/package.json b/apps/website/package.json index f37274025..954cc574d 100644 --- a/apps/website/package.json +++ b/apps/website/package.json @@ -8,6 +8,7 @@ "@devlaunchers/dev-recruiters": "workspace:^", "@devlaunchers/ideaspace": "*", "@devlaunchers/site-projects": "*", + "@devlaunchers/utility": "workspace:*", "@ramonak/react-progress-bar": "^5.0.2", "@types/styled-components": "5.1.25", "articles": "^0.2.2", @@ -35,11 +36,10 @@ "react-tabs": "^3.1.0", "react-toastify": "latest", "regenerator-runtime": "^0.13.9", - "semantic-release": "^19.0.3", "styled-components": "5.3.5", "styled-normalize": "^8.0.7", "tabletop": "1.6.3", - "wrangler": "2.14.0" + "wrangler": "^3.0.1" }, "devDependencies": { "@devlaunchers/eslint-config-bases": "workspace:^", @@ -50,8 +50,6 @@ "eslint-config-airbnb-base": "^15.0.0", "eslint-config-next": "^12.2.3", "eslint-config-prettier": "^8.5.0", - "husky": "^8.0.1", - "lint-staged": "^13.0.3", "postcss": "^8.4.14", "prettier": "^2.7.1", "typescript": "^4.7.4", @@ -63,7 +61,6 @@ "start": "next start", "prettier": "prettier --write \"./**/*.{js,jsx,json}\"", "pre-commit": "eslint '*/**/*.{js,jsx}' --fix", - "prepare": "husky install", "clean": "rimraf --no-glob ./tsconfig.tsbuildinfo", "lint": "eslint . --ext .ts,.tsx,.js,.jsx,.cjs,.mjs,.mdx", "fix-all-files": "eslint . --ext .ts,.tsx,.js,.jsx,.cjs,.mjs,.mdx --fix", @@ -78,27 +75,7 @@ "not ie <= 11", "not op_mini all" ], - "lint-staged": { - "*.js": "eslint '*/**/*.{js,jsx}' --fix", - "*.{js,css,md}": "prettier --write" - }, "resolutions": { "styled-components": "^5" - }, - "release": { - "branches": [ - "release" - ], - "plugins": [ - "@semantic-release/commit-analyzer", - "@semantic-release/release-notes-generator", - [ - "@semantic-release/npm", - { - "npmPublish": false - } - ], - "@semantic-release/github" - ] } } diff --git a/apps/website/src/components/common/Calendar/Calendar.js b/apps/website/src/components/common/Calendar/Calendar.js deleted file mode 100644 index 99fb18f65..000000000 --- a/apps/website/src/components/common/Calendar/Calendar.js +++ /dev/null @@ -1,100 +0,0 @@ -import React, { useState } from "react"; -import axios from "axios"; -import { DateTime } from "luxon"; -import { Wrapper, Event, Day, WeekdayTitle } from "./StyledCalendar"; - -const Calendar = ({ URL }) => { - // URL prop passed in from WeeksGlance component - const [eventList, setEventList] = useState([]); - - const current = DateTime.now(); - const max = current.plus({ days: 7 }); // max is 7 days from today - - /* sends a get request to Google Calendar API and parses the returned JSON to populate a new array with only the properties we need to display */ - - const makeRequest = () => { - axios - .get(URL) - .then((response) => { - const tempEventList = []; - response.data.items.forEach((entry) => { - // get the name, time, and weekday of each event and push them in to an array of Event objects. - const time = DateTime.fromISO(entry.start.dateTime, { - zone: entry.start.timeZone, - }).setZone(); - tempEventList.push({ - name: entry.summary, - time: time.toFormat("t"), - weekday: time.weekday, - }); - }); - - setEventList(tempEventList); - }) - .catch(() => {}); - }; - - React.useEffect(makeRequest, []); - - const weekdays = ["MON", "TUES", "WED", "THURS", "FRI", "SAT", "SUN"]; - const dates = []; - - /* first stores the dates of the previous weekdays in the Array, followed by today, followed by the remaining days in this week */ - const createDate = () => { - const todayInt = current.weekday; - for (let i = 1; i < todayInt; i++) { - dates.push(max.minus({ days: todayInt - i }).day); - } - dates.push(current.day); - - for (let i = todayInt + 1; i < 8; i++) { - dates.push(current.plus({ days: i - todayInt }).day); - } - - return dates; - }; - const dateslist = createDate(); - - return ( - - {weekdays.map((day, i) => { - // map through each day in the weekday array, and for each day, map through that day's list of events - const date = dateslist[i]; - return ( - - -
      {day}
      - -
      - {date} -
      -
      - {eventList.map(({ name, time, weekday }) => { - // destructuring each object in eventList to render just the name and time of the event - if (weekday === i + 1) { - return ( - - {name} -
      - {time} -
      - ); - } - return null; - })} - {eventList.filter(({ weekday }) => weekday === i + 1).length === // if there are no events on that day, display "No events" - 0 ? ( -
      - No events -
      - ) : ( - "" - )} -
      - ); - })} -
      - ); -}; - -export default Calendar; diff --git a/apps/website/src/components/common/Calendar/StyledCalendar.js b/apps/website/src/components/common/Calendar/StyledCalendar.js deleted file mode 100644 index 6eade9ebb..000000000 --- a/apps/website/src/components/common/Calendar/StyledCalendar.js +++ /dev/null @@ -1,57 +0,0 @@ -import Styled from "styled-components"; - -import theme from "../../../styles/theme"; - -export const Wrapper = Styled.div` - display: flex; - grid-area: WeeksGlance; - flex-direction: row; - width: 100%; -background-color: #DAD8D9; - - @media (orientation: portrait) { - flex-direction: column; - } -;`; - -export const Day = Styled.div` - display: flex; - color: #1c1c1c; - font-weight: 500; - padding-bottom: .5rem; - flex-direction: column; - font-size: 20px; - background-color: transparent; - width: 15%; - - @media (orientation: portrait) { - width:100%; - } -`; - -export const Event = Styled.div` - display: flex; - flex-wrap: wrap; - overflow: hidden; - white-space: nowrap; - background: bisque; - color: #f0edee; - width: 98%; - background-color: #ff7f0e; - margin-top: 1rem; - font-size: .9rem; - justify-content: center; - text-align: center; - padding-top: .3rem; - padding-bottom: .3rem; - font-family: roboto; -`; - -export const WeekdayTitle = Styled.div` - font-family: ${theme.fonts.headline}; - background-color: #f0edee; - color: #1c1c1c; - font-weight: 500; - padding-left: .5rem; - -`; diff --git a/apps/website/src/components/common/Calendar/index.js b/apps/website/src/components/common/Calendar/index.js deleted file mode 100644 index 6c64b5c40..000000000 --- a/apps/website/src/components/common/Calendar/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from "./Calendar"; diff --git a/apps/website/src/components/common/DiscordAvatar/DiscordAvatar.js b/apps/website/src/components/common/DiscordAvatar/DiscordAvatar.js deleted file mode 100644 index b6010fa37..000000000 --- a/apps/website/src/components/common/DiscordAvatar/DiscordAvatar.js +++ /dev/null @@ -1,13 +0,0 @@ -import { Avatar } from "./StyledDiscordAvatar"; - -export default function DiscordAvatar({ discordId, avatarKey }) { - return ( - - ); -} diff --git a/apps/website/src/components/common/DiscordAvatar/StyledDiscordAvatar.js b/apps/website/src/components/common/DiscordAvatar/StyledDiscordAvatar.js deleted file mode 100644 index 454686740..000000000 --- a/apps/website/src/components/common/DiscordAvatar/StyledDiscordAvatar.js +++ /dev/null @@ -1,9 +0,0 @@ -import styled from "styled-components"; - -export const Avatar = styled.img.attrs(() => ({ - alt: "Discord Profile Picture" -}))` - width: 50%; - border-radius: 50%; - align-self: end; -`; diff --git a/apps/website/src/components/common/DiscordAvatar/index.js b/apps/website/src/components/common/DiscordAvatar/index.js deleted file mode 100644 index a9d147176..000000000 --- a/apps/website/src/components/common/DiscordAvatar/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from "./DiscordAvatar"; diff --git a/apps/website/src/components/common/Footer/Newsletter/Newsletter.js b/apps/website/src/components/common/Footer/Newsletter/Newsletter.js index bbd667e08..6750ee1cf 100644 --- a/apps/website/src/components/common/Footer/Newsletter/Newsletter.js +++ b/apps/website/src/components/common/Footer/Newsletter/Newsletter.js @@ -9,7 +9,6 @@ import { Secret, } from "./StyledNewsletter"; import validateEmail from "../../../../utils/ValidateEmail"; -import { env } from "../../../../utils/EnvironmentVariables"; const NewsLetter = (props) => { const [email, setEmail] = useState(""); @@ -53,7 +52,7 @@ const NewsLetter = (props) => { }} > →{" "} - + 😄 {" "} ← diff --git a/apps/website/src/components/common/HamburgerMenu/HamburgerMenu.js b/apps/website/src/components/common/HamburgerMenu/HamburgerMenu.js index 85d30fa8d..532bab62a 100644 --- a/apps/website/src/components/common/HamburgerMenu/HamburgerMenu.js +++ b/apps/website/src/components/common/HamburgerMenu/HamburgerMenu.js @@ -5,11 +5,10 @@ import { slide as SlideHamburgerMenu } from "react-burger-menu"; import style from "./HamburgerMenu.module.css"; import logoMonogramImage from "../../../images/logo-monogram.png?webp"; import Logout from "../../../utils/Logout"; -import { env } from "../../../utils/EnvironmentVariables"; -import { useUserDataContext } from "../../../context/UserDataContext"; +import { useUserDataContext } from "@devlaunchers/components/context/UserDataContext"; function HamburgerMenu() { - const { userData } = useUserDataContext(); + const { userData, setUserData } = useUserDataContext(); const [menuOpen, setMenuOpen] = React.useState(false); // Called when the open/close state of the menu changes (onStateChange callback) @@ -22,6 +21,26 @@ function HamburgerMenu() { setMenuOpen(false); }; + const handleLogout = () => { + Logout(); + setUserData({ + id: 0, + name: '', + username: '', + email: '', + bio: '', + profilePictureUrl: '', + socialMediaLinks: [], + discord: { + id: 0, + avatar: '', + username: '', + discriminator: '', + }, + interests: [], + }) + }; + return ( ) : ( - +
      SIGN IN
      )} diff --git a/apps/website/src/components/common/Header/AccountDropdown/AccountDropdown.js b/apps/website/src/components/common/Header/AccountDropdown/AccountDropdown.js index 04cb0f8b1..9c7426f17 100644 --- a/apps/website/src/components/common/Header/AccountDropdown/AccountDropdown.js +++ b/apps/website/src/components/common/Header/AccountDropdown/AccountDropdown.js @@ -1,14 +1,36 @@ import React from "react"; import Link from "next/link"; import Logout from "../../../../utils/Logout"; -import { env } from "../../../../utils/EnvironmentVariables"; import { AccountMenuDropdownItem, AccountMenuDropdownButton, MenuButton, } from "./StyledAccountDropdown"; +import { useUserDataContext } from "@devlaunchers/components/context/UserDataContext"; export default function AccountDropdown(props) { + const { setUserData } = useUserDataContext(); + + const handleLogout = () => { + Logout(); + setUserData({ + id: 0, + name: '', + username: '', + email: '', + bio: '', + profilePictureUrl: '', + socialMediaLinks: [], + discord: { + id: 0, + avatar: '', + username: '', + discriminator: '', + }, + interests: [], + }) + }; + return (
      {props?.userData?.id && props?.userData?.username ? ( @@ -21,14 +43,16 @@ export default function AccountDropdown(props) { Visit Account Page - + Logout } > ) : ( - + Sign In )} diff --git a/apps/website/src/components/common/Header/Header.js b/apps/website/src/components/common/Header/Header.js index 541d4b327..9713f1980 100644 --- a/apps/website/src/components/common/Header/Header.js +++ b/apps/website/src/components/common/Header/Header.js @@ -1,7 +1,6 @@ import React from "react"; import Link from "next/link"; import AccountDropdown from "./AccountDropdown"; - import { HeaderBlock, Logo, @@ -12,13 +11,9 @@ import { HeaderNav, NavEntry, } from "./StyledHeader"; - -import { useUserDataContext } from "../../../context/UserDataContext"; import HamburgerMenu from "../HamburgerMenu"; export default function Header() { - const { userData } = useUserDataContext(); - return ( @@ -61,7 +56,7 @@ export default function Header() {
      - + ); diff --git a/apps/website/src/components/modules/Home/Home.js b/apps/website/src/components/modules/Home/Home.js index 7a7d8fd2f..5742f751f 100644 --- a/apps/website/src/components/modules/Home/Home.js +++ b/apps/website/src/components/modules/Home/Home.js @@ -18,6 +18,7 @@ import { ColoredCtaEntry, ColoredCtaEntryImage, ColoredCtaEntryTitle, + Wrapper, } from "./StyledHome"; import CtaDescriptionArea from "./CtaDescriptionArea"; @@ -36,26 +37,8 @@ function Home({ theme }) { const createRef = useRef(null); const donateRef = useRef(null); - React.useEffect(() => { - setTimeout(() => { - toast( - <> - Hey there! Are you a developer? Want management experience in a friendly, supportive environment? We're currently searching for intermediate and advanced devs to take on leadership positions in our product teams! - , - { - bodyClassName: "toast-body", - position: "bottom-right", - autoClose: 25000, - closeOnClick: true, - pauseOnHover: true, - draggable: true, - } - ); - }, 6000) - }, []); - return ( -
      + @@ -223,7 +206,7 @@ function Home({ theme }) { -
      + ); } diff --git a/apps/website/src/components/modules/Home/StyledHome.js b/apps/website/src/components/modules/Home/StyledHome.js index 21379333f..a651f062b 100644 --- a/apps/website/src/components/modules/Home/StyledHome.js +++ b/apps/website/src/components/modules/Home/StyledHome.js @@ -1,5 +1,14 @@ import styled from "styled-components"; + +export const Wrapper = styled.div` + img { + max-width: none; + } + + +` + export const HomePageBody = styled.div` /*margin-top: 45vw;*/ width: 100%; diff --git a/apps/website/src/components/modules/SignUp/SignUp.js b/apps/website/src/components/modules/SignUp/SignUp.js index d21ea44e6..2251edc0e 100644 --- a/apps/website/src/components/modules/SignUp/SignUp.js +++ b/apps/website/src/components/modules/SignUp/SignUp.js @@ -3,11 +3,8 @@ import { useRouter } from "next/router"; import axios from "axios"; import PageBody from "../../common/PageBody"; // import DiscordImage from "../../../images/signup/discord.png?webp"; - import { FormWrapper } from "./StyledSignUp"; - -import { useUserDataContext } from "../../../context/UserDataContext"; -import { env } from "../../../utils/EnvironmentVariables"; +import { useUserDataContext } from "@devlaunchers/components/context/UserDataContext"; export default function SignUp() { // const [loading, setLoading] = useState(true); @@ -83,7 +80,7 @@ export default function SignUp() {

      Connect your Discord account! (optional)

      - + discord + @@ -51,6 +52,6 @@ export default function SupportUs() { -
      + ); } diff --git a/apps/website/src/components/modules/UserProfile/Opportunities/Opportunities.js b/apps/website/src/components/modules/UserProfile/Opportunities/Opportunities.js deleted file mode 100644 index be07c1682..000000000 --- a/apps/website/src/components/modules/UserProfile/Opportunities/Opportunities.js +++ /dev/null @@ -1,74 +0,0 @@ -import React from "react"; -import axios from "axios"; -import Link from "next/link"; -import { useRouter } from "next/router"; - -import Button from "../../../common/Button"; -import Card from "../../../common/Card"; - -import { env } from "../../../../utils/EnvironmentVariables"; - -import { useUserDataContext } from "../../../../context/UserDataContext"; - -import { OpportunitiesContainer } from "./StyledOpportunities"; - -const Opportunities = ({ opportunities }) => { - return ( - - {opportunities?.length > 0 ? ( -
      -

      Recommended Opportunities For You

      -
      - Contribute to a Dev Launchers project to build impactful products - alongside others and gain experience that will carry you forward - into your goals! -
      -
      - {opportunities.map((opportunity) => ( -
      -

      - {opportunity.title}{" "} - - ({opportunity.level} Opportunity) - -

      -
      - Remote -
      -
      {opportunity.project.title} Project
      -
      - - {opportunity.description.substring(0, 200) + - (opportunity.description.length > 200 ? "..." : "")} - -
      -
      - ))} -
      -
      - ) : ( -
      - )} - - ); -}; - -export default Opportunities; diff --git a/apps/website/src/components/modules/UserProfile/People/People.js b/apps/website/src/components/modules/UserProfile/People/People.js index 89e7d97b6..bf0728b3f 100644 --- a/apps/website/src/components/modules/UserProfile/People/People.js +++ b/apps/website/src/components/modules/UserProfile/People/People.js @@ -1,16 +1,5 @@ import React from "react"; import Image from "next/image"; -import axios from "axios"; -import Link from "next/link"; -import { useRouter } from "next/router"; - -import Button from "../../../common/Button"; -import Card from "../../../common/Card"; - -import { env } from "../../../../utils/EnvironmentVariables"; - -import { useUserDataContext } from "../../../../context/UserDataContext"; - import { PeopleContainer } from "./StyledPeople"; const People = ({ people }) => { diff --git a/apps/website/src/components/modules/UserProfile/People/StyledPeople.js b/apps/website/src/components/modules/UserProfile/People/StyledPeople.js deleted file mode 100644 index 49fa45de2..000000000 --- a/apps/website/src/components/modules/UserProfile/People/StyledPeople.js +++ /dev/null @@ -1,12 +0,0 @@ -import styled, { css } from "styled-components"; - -export const PeopleContainer = styled.div` - /* width: 100%; */ - color: ${({ theme }) => theme.colors.NEUTRAL_2}; - background-color: ${({ theme }) => theme.colors.NEUTRAL_1}; - padding: 2rem; - - h3 { - color: ${({ theme }) => theme.colors.NEUTRAL_2}; - } -`; diff --git a/apps/website/src/components/modules/UserProfile/ProfileCard/ProfileCard.js b/apps/website/src/components/modules/UserProfile/ProfileCard/ProfileCard.js deleted file mode 100644 index fabf1b570..000000000 --- a/apps/website/src/components/modules/UserProfile/ProfileCard/ProfileCard.js +++ /dev/null @@ -1,12 +0,0 @@ -import React from "react"; -import { Wrapper, ProfilePicture, Name, UserName } from "./StyledProfileCard"; - -const UserProfileImage = ({ img, name, username }) => ( - - - {name} - {username} - -); - -export default UserProfileImage; diff --git a/apps/website/src/components/modules/UserProfile/RecommendedIdeas/RecommendedIdeas.js b/apps/website/src/components/modules/UserProfile/RecommendedIdeas/RecommendedIdeas.js deleted file mode 100644 index ef1363c91..000000000 --- a/apps/website/src/components/modules/UserProfile/RecommendedIdeas/RecommendedIdeas.js +++ /dev/null @@ -1,70 +0,0 @@ -import React from 'react'; -import axios from 'axios'; -import Link from 'next/link'; -import { useRouter } from 'next/router'; - -import Button from '../../../common/Button'; -import Card from '../../../common/Card'; - -import { env } from '../../../../utils/EnvironmentVariables'; - -import { useUserDataContext } from '../../../../context/UserDataContext'; - -import { - RecommendedIdeasContainer, - RecommendedIdeaCardContainer, -} from './StyledRecommendedIdeas'; - -const RecommendedIdeas = ({ ideas }) => { - return ( - - {ideas?.length > 0 ? ( -
      -

      Recommended Ideas 💡

      -
      - Have any thoughts on these projects Dev Launchers members may be - tackling in the near future? Help us make them the best they can be - with your opinions and feedback! -
      - -
      - {ideas.map((idea) => ( - - skill), - description: - idea.description.substring(0, 200) + - (idea.description.length > 200 ? '...' : ''), - href: `/ideaspace/workshopping/${idea.id}`, - }} - /> - - ))} -
      -
      - ) : ( - '' - )} -
      - ); -}; - -export default RecommendedIdeas; diff --git a/apps/website/src/components/modules/UserProfile/UserProfile.js b/apps/website/src/components/modules/UserProfile/UserProfile.js deleted file mode 100644 index 5841ed867..000000000 --- a/apps/website/src/components/modules/UserProfile/UserProfile.js +++ /dev/null @@ -1,290 +0,0 @@ -import React from "react"; -import { useEffect, useState } from 'react'; -import "react-tabs/style/react-tabs.css"; // import react-tabs styles -import { Tabs, Tab, TabList, TabPanel } from "react-tabs"; -import axios from "axios"; - -import Button from '../../common/Button'; -import PageBody from '../../common/PageBody'; - -import { useUserDataContext } from '../../../context/UserDataContext'; -import BioBox from './BioBox'; -import Opportunities from './Opportunities'; -import ProfileCard from './ProfileCard'; -import RecommendedIdeas from './RecommendedIdeas'; -import UserProjects from './UserProjects'; -import People from './People'; - -import { env } from '../../../utils/EnvironmentVariables'; - -import { Misc, UserInfo, UserSection, Wrapper } from './StyledUserProfile'; -import UserInterests from './UserInterests'; -import { useRouter } from "next/router"; -// import DiscordSection from "./DiscordSection/DiscordSection"; - -// State management component -export default function UserProfile({ otherUser }) { - - const { userData, isAuthenticated } = useUserDataContext(); - const [loading, setLoading] = useState(true); - const [opportunities, setOpportunities] = React.useState([]); - const [myProjects, setMyProjects] = React.useState([]); - const [projects, setProjects] = React.useState([]); - const [ideas, setIdeas] = React.useState([]); - const [people, setPeople] = React.useState([]); - const [interests, setInterests] = React.useState([]); - - // If user hasn't set a username, redirect them to the signup form - const router = useRouter(); - React.useEffect(() => { - if (isAuthenticated && userData.name === '') - router.push("/signup"); - }, [isAuthenticated]); - - // Start Projects/Opportunities - React.useEffect(() => { - getProjectData(); - }, []); - const getProjectData = async () => { - await axios(`${process.env.NEXT_PUBLIC_STRAPI_URL}/projects`) - .then(({ data }) => { - if (data) { - setProjects(data); - - const tempOpportunities = []; - data.map((project) => { - project.opportunities.map((opportunity) => { - opportunity.project = project; - tempOpportunities.push(opportunity); - }); - }); - setOpportunities(tempOpportunities); - } - }) - .catch(() => { - console.error("Could not fetch project data"); - }); - }; - React.useEffect(() => { - const myProjects = []; - projects.map((project) => { - [...project.team.leaders, ...project.team.members].map((member) => { - if (member.id == userData.id) myProjects.push(project); - }); - }); - setMyProjects(myProjects); - }, [projects, userData]); - // End Projects/Opportunities - - - // Start Ideas - React.useEffect(() => { - getIdeaData(); - }, []); - const getIdeaData = async () => { - await axios(`${process.env.NEXT_PUBLIC_STRAPI_URL}/idea-cards`) - .then(({ data }) => { - if (data) { - setIdeas(data); - } - }) - .catch(() => { - console.error("Could not fetch idea data"); - }); - }; - // End Ideas - - - // Start People - React.useEffect(() => { - getPeopleData(); - }, []); - const getPeopleData = async () => { - const userCount = (await axios(`${process.env.NEXT_PUBLIC_STRAPI_URL}/users/count`)).data; - let randomUserIds = [ - parseInt(Math.random()*userCount), - parseInt(Math.random()*userCount), - parseInt(Math.random()*userCount), - parseInt(Math.random()*userCount), - parseInt(Math.random()*userCount), - parseInt(Math.random()*userCount), - ]; - console.log(randomUserIds); - let usersData = await Promise.all(randomUserIds.map(async (userId) => - (await axios(`${process.env.NEXT_PUBLIC_STRAPI_URL}/users/${userId}`)).data - )); - - console.log(usersData); - setPeople(usersData); - } - // End People - - // Start Interests - React.useEffect(() => { - getInterests(); - }, []); - const getInterests = async () => { - await axios(`${process.env.NEXT_PUBLIC_STRAPI_URL}/interests`) - .then(({ data }) => { - if (data) { - setInterests(data); - } - }) - .catch(() => { - console.error("Could not fetch interest data"); - }); - }; - // End Interests - - - useEffect(() => { - setLoading(userData?.id === -1 || otherUser?.id === -1); - }, [otherUser, userData]); - - return ; -} - -// View component -export function UserProfileView({ - otherUser, - userData, - loading, - opportunities, - myProjects, - projects, - ideas, - people, - interests -}) { - - if (loading) { - return Loading.....; - } - - return ( - - {userData?.id || (otherUser?.id && !loading) ? ( - - - - - - {/* } - - { */} - - - - - {/* - - */} - - - - - { - // Have to do this hack for some reason (create empty tab if page not loaded)... - // otherwise tabs break - Object.entries(userData || {}).length === 0 ? : "" - } - { - // Render tabs from our dynamically built learnPageData object - ["Projects", "People", "Interests", "Ideas", "Opportunities"].map((key) => ( - {key} - )) - } - - - - - - - - - - - - - - - - - - - - - - - - - {/* }{ */} - {/* - - */} - {/* - */} - - - ) : ( -
      -

      - Please sign in to access this page! -

      - -
      -
      - )} -
      -
      - ); -} - - diff --git a/apps/website/src/components/modules/UserProfile/UserProjects/UserProjects.js b/apps/website/src/components/modules/UserProfile/UserProjects/UserProjects.js deleted file mode 100644 index d2dbb53b2..000000000 --- a/apps/website/src/components/modules/UserProfile/UserProjects/UserProjects.js +++ /dev/null @@ -1,88 +0,0 @@ -import React from "react"; -import axios from "axios"; -import Link from "next/link"; -import { useRouter } from "next/router"; - -import Button from "../../../common/Button"; -import Card from "../../../common/Card"; - -import { env } from "../../../../utils/EnvironmentVariables"; - -import { useUserDataContext } from "../../../../context/UserDataContext"; - -import { UserProjectsContainer } from "./StyledUserProjects"; - -const UserProjects = ({ myProjects }) => { - const noProjectsDisplay = ( -
      -

      - It looks like you're not currently part of any of our Product Teams... -

      -
      - -
      - ); - - return ( - - {myProjects?.length > 0 ? ( -
      -

      My Project Teams

      -
      - {myProjects.map((project) => ( - keyword), - description: project.catchPhrase, - href: `https://devlaunchers.org/projects/${project.slug}`, - imageSrc: project.heroImage?.url, - /* - actions: ( - <> - - LEARN MORE - - - DONATE - - - ), - */ - }} - /> - ))} -
      -
      - ) : ( - noProjectsDisplay - )} -
      - ); -}; - -export default UserProjects; diff --git a/apps/website/src/context/ProjectsContext.js b/apps/website/src/context/ProjectsContext.js index d375ff2cf..1bd16c774 100644 --- a/apps/website/src/context/ProjectsContext.js +++ b/apps/website/src/context/ProjectsContext.js @@ -2,8 +2,6 @@ import React from "react"; import constate from "constate"; // State Context Object Creator import axios from "axios"; -import { env } from "../utils/EnvironmentVariables"; - const DEFAULT_PROJECTS = []; // Built from this article: https://www.sitepoint.com/replace-redux-react-hooks-context-api/ diff --git a/apps/website/src/context/UserDataContext.js b/apps/website/src/context/UserDataContext.js deleted file mode 100644 index ee71862d8..000000000 --- a/apps/website/src/context/UserDataContext.js +++ /dev/null @@ -1,67 +0,0 @@ -import axios from 'axios'; -import constate from 'constate'; // State Context Object Creator -import React from 'react'; - -import { env } from '../utils/EnvironmentVariables'; - -const DEFAULT_USER = { - id: 0, - name: '', - username: '', - email: '', - bio: '', - profilePictureUrl: '', - socialMediaLinks: [], - totalPoints: 0, - totalSeasonPoints: 0, - availablePoints: 0, - volunteerHours: 0, - discord: { - id: 0, - avatar: '', - username: '', - discriminator: '', - }, - interests: [], -}; - -// Built from this article: https://www.sitepoint.com/replace-redux-react-hooks-context-api/ - -// Step 1: Create a custom hook that contains your state and actions -function useUserData() { - const [userData, setUserData] = React.useState(DEFAULT_USER); - const [isAuthenticated, setIsAuthenticated] = React.useState(); - - React.useEffect(() => { - axios(`${env().STRAPI_URL}/users/me`, { - withCredentials: true, - }) - .then(({ data: currentUser }) => { - setUserData({ - id: currentUser.id, - name: currentUser.profile.displayName, - username: currentUser.username, - email: currentUser.email, - bio: currentUser.profile.bio, - profilePictureUrl: currentUser.profile.profilePictureUrl, - socialMediaLinks: currentUser.profile.socialMediaLinks, - totalPoints: currentUser.point.totalPoints, - totalSeasonPoints: currentUser.point.totalSeasonPoints, - availablePoints: currentUser.point.availablePoints, - volunteerHours: currentUser.point.volunteerHours, - interests: currentUser.interests, - }); - setIsAuthenticated(true); - }) - .catch(() => { - // setUserData({ id: "invalid" }); - setIsAuthenticated(false); - }); - }, []); - - return { userData, setUserData, isAuthenticated }; -} - -// Step 2: Declare your context state object to share the state with other components -const [UserDataProvider, useUserDataContext] = constate(useUserData); -export { UserDataProvider, useUserDataContext }; diff --git a/apps/website/src/pages/_app.js b/apps/website/src/pages/_app.js index 25419a90b..0d263aa31 100644 --- a/apps/website/src/pages/_app.js +++ b/apps/website/src/pages/_app.js @@ -10,7 +10,6 @@ import 'react-toastify/dist/ReactToastify.css'; import theme from '../styles/theme'; import { initGA, logPageView } from '../utils/GoogleAnalytics'; -import { UserDataProvider } from '../context/UserDataContext'; import { SheetsProvider } from '../context/SheetsContext'; const hashRedirect = (router) => { @@ -45,32 +44,30 @@ function MyApp(props) { return ( <> - - - - -
      - - - + + + +
      + + + -
      - -
      - {/* */} - {props.children} +
      +
      - - - + {/* */} + {props.children} +
      +
      +
      ); } diff --git a/apps/website/src/pages/index.js b/apps/website/src/pages/index.js index 1e78063c4..8e16ca678 100644 --- a/apps/website/src/pages/index.js +++ b/apps/website/src/pages/index.js @@ -15,7 +15,7 @@ export default function Index() { > - + - + -
      -
      ); } diff --git a/apps/website/src/pages/projects/[slug].jsx b/apps/website/src/pages/projects/[slug].jsx index 0b071cfd0..23039e502 100644 --- a/apps/website/src/pages/projects/[slug].jsx +++ b/apps/website/src/pages/projects/[slug].jsx @@ -1,3 +1,3 @@ -export { getStaticProps } from "@devlaunchers/site-projects/src/pages/[slug]"; -export { getStaticPaths } from "@devlaunchers/site-projects/src/pages/[slug]"; -export { default } from "@devlaunchers/site-projects/src/pages/[slug]"; +export { getStaticProps } from '@devlaunchers/site-projects/src/pages/[slug]'; +export { getStaticPaths } from '@devlaunchers/site-projects/src/pages/[slug]'; +export { default } from '@devlaunchers/site-projects/src/pages/[slug]'; diff --git a/apps/website/src/pages/users/[username].js b/apps/website/src/pages/users/[username].js deleted file mode 100644 index a8a652fb9..000000000 --- a/apps/website/src/pages/users/[username].js +++ /dev/null @@ -1,54 +0,0 @@ -import React from "react"; - -import axios from "axios"; -import Header from "../../components/common/Header"; -import UserProfile from "../../components/modules/UserProfile"; -import Footer from "../../components/common/Footer"; -import { env } from "../../utils/EnvironmentVariables"; - -export const getStaticPaths = async () => { - const { data } = await axios(`${process.env.NEXT_PUBLIC_STRAPI_URL}/users`, { - headers: { - Accept: "application/json, text/plain, */*", - "User-Agent": - "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.107 Safari/537.36", - }, - }); - - const paths = data - .filter(({ username }) => username) - .map(({ username }) => ({ - params: { username }, - })); - - return { - paths, - fallback: "blocking", - }; -}; - -export const getStaticProps = async (context) => { - const { username } = context.params; - const { data: user } = await axios( - `${process.env.NEXT_PUBLIC_STRAPI_URL}/users?username_eq=${encodeURI(username)}` - ); - - if (!user) { - return { - notFound: true, - }; - } - - return { - props: { user: user[0] }, - revalidate: 20, - }; -}; - -export default function UserProfilePage({ user }) { - return ( -
      - -
      - ); -} diff --git a/apps/website/src/pages/users/me.js b/apps/website/src/pages/users/me.js deleted file mode 100644 index 9cc28982c..000000000 --- a/apps/website/src/pages/users/me.js +++ /dev/null @@ -1,13 +0,0 @@ -import React from "react"; - -import Header from "../../components/common/Header"; -import UserProfile from "../../components/modules/UserProfile"; -import Footer from "../../components/common/Footer"; - -export default function UserProfilePage() { - return ( -
      - -
      - ); -} diff --git a/apps/website/src/utils/Logout.js b/apps/website/src/utils/Logout.js index 9a33f05c7..855b1929f 100644 --- a/apps/website/src/utils/Logout.js +++ b/apps/website/src/utils/Logout.js @@ -1,6 +1,5 @@ import Router from "next/router"; import axios from "axios"; -import { env } from "./EnvironmentVariables"; const Logout = () => { axios diff --git a/monorepo.code-workspace b/monorepo.code-workspace index a34d5e4d9..c13fd5e66 100644 --- a/monorepo.code-workspace +++ b/monorepo.code-workspace @@ -12,6 +12,10 @@ "name": "ideaspace", "path": "apps/ideaspace" }, + { + "name": "user-profile", + "path": "apps/user-profile" + }, { "name": "site-projects", "path": "apps/site-projects" @@ -65,6 +69,7 @@ "./apps/app", "./apps/website", "./apps/ideaspace", + "./apps/user-profile", "./apps/site-projects", "./apps/dev-recruiters", "./packages/UI", diff --git a/package.json b/package.json index 83853494e..315dc269b 100644 --- a/package.json +++ b/package.json @@ -68,7 +68,7 @@ "commitizen": "4.2.5", "cz-conventional-changelog": "3.3.0", "eslint": "8.21.0", - "husky": "8.0.1", + "husky": "9.0.11", "is-ci": "3.0.1", "lint-staged": "13.0.3", "msw": "0.48.0", diff --git a/packages/UI/.babelrc b/packages/UI/.babelrc index b3d3c4523..2c055d079 100644 --- a/packages/UI/.babelrc +++ b/packages/UI/.babelrc @@ -1,3 +1,16 @@ { - "plugins": ["babel-plugin-styled-components"] - } \ No newline at end of file + "sourceType": "unambiguous", + "presets": [ + [ + "@babel/preset-env", + { + "targets": { + "chrome": 100 + } + } + ], + "@babel/preset-typescript", + "@babel/preset-react" + ], + "plugins": ["babel-plugin-styled-components"] +} diff --git a/packages/UI/.eslintrc.js b/packages/UI/.eslintrc.js index 6dd764ea8..bbde0fbcb 100644 --- a/packages/UI/.eslintrc.js +++ b/packages/UI/.eslintrc.js @@ -25,6 +25,7 @@ module.exports = { '@devlaunchers/eslint-config-bases/rtl', '@devlaunchers/eslint-config-bases/storybook', '@devlaunchers/eslint-config-bases/playwright', + '@devlaunchers/eslint-config-bases/tailwind', // Add specific rules for your framework if needed. // ie: @@ -38,6 +39,13 @@ module.exports = { ], rules: { // Specific global rules for your app or package + 'tailwindcss/no-custom-classname': [ + 'error', + { + // if you want to use a class that isn't tailwind specific (NOT RECOMMENDED) make sure to add it here + whitelist: [], + }, + ], }, overrides: [ // Specific file rules for your app or package diff --git a/packages/UI/.storybook/main.js b/packages/UI/.storybook/main.js deleted file mode 100644 index 4cf1c598c..000000000 --- a/packages/UI/.storybook/main.js +++ /dev/null @@ -1,21 +0,0 @@ -// use `mergeConfig` to recursively merge Vite options -module.exports = { - stories: ['../src/**/*.stories.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'], - staticDirs: ['../public'], - addons: [ - '@storybook/addon-essentials', - '@storybook/addon-a11y', - '@storybook/addon-links', - 'storybook-addon-next-router', - '@storybook/addon-viewport', - ], - framework: '@storybook/react', - webpackFinal: async (config) => { - config.module.rules.push({ - test: /\.mjs$/, - include: /node_modules/, - type: "javascript/auto", - }); - return config; - } -}; diff --git a/packages/UI/.storybook/main.ts b/packages/UI/.storybook/main.ts new file mode 100644 index 000000000..b825666bf --- /dev/null +++ b/packages/UI/.storybook/main.ts @@ -0,0 +1,54 @@ +import type { StorybookConfig } from '@storybook/nextjs'; +import path, { dirname, join } from 'path'; + +const config: StorybookConfig = { + stories: ['../src/**/*.stories.@(js|jsx|ts|tsx)', '../src/**/*.mdx'], + staticDirs: ['../public'], + addons: [ + '@storybook/addon-essentials', + '@storybook/addon-a11y', + '@storybook/addon-links', + '@storybook/addon-mdx-gfm', + '@storybook/addon-designs', + 'storybook-addon-pseudo-states' + ].map(getAbsolutePath), + framework: { + name: getAbsolutePath('@storybook/nextjs') as "@storybook/nextjs", + options: {}, + }, + webpackFinal: async (config) => { + config?.module?.rules?.push({ + test: /\.mjs$/, + include: /node_modules/, + type: 'javascript/auto', + }); + return config; + }, + docs: { + autodocs: true, + }, + typescript: { + reactDocgenTypescriptOptions: { + compilerOptions: { + allowSyntheticDefaultImports: false, + esModuleInterop: false, + }, + shouldExtractLiteralValuesFromEnum: true, + propFilter: (prop) => { + // pull radix-ui props declaration files + if (prop.parent?.fileName.includes('@radix-ui')) return true; + // ignore rest of node_modules props declaration file + return (prop.parent ? !/node_modules/.test(prop.parent.fileName) : true) + }, + }, + reactDocgen: 'react-docgen-typescript', + // skipBabel: true, + check: true, + } +}; + +function getAbsolutePath(value) { + return dirname(require.resolve(join(value, 'package.json'))); +} + +export default config; \ No newline at end of file diff --git a/packages/UI/.storybook/preview-head.html b/packages/UI/.storybook/preview-head.html index 1e51884d7..171fd5946 100644 --- a/packages/UI/.storybook/preview-head.html +++ b/packages/UI/.storybook/preview-head.html @@ -7,3 +7,7 @@ href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.13.0/css/all.min.css" rel="stylesheet" /> + diff --git a/packages/UI/.storybook/preview.jsx b/packages/UI/.storybook/preview.jsx index 95fb83c74..06590a7bb 100644 --- a/packages/UI/.storybook/preview.jsx +++ b/packages/UI/.storybook/preview.jsx @@ -1,79 +1,110 @@ -import React from "react"; -import { RouterContext } from "next/dist/shared/lib/router-context"; -import { ThemeProvider } from "styled-components"; -import GlobalStyle from "../src/styles/global"; -import theme from "../src/styles/theme"; -import * as NextImage from "next/image"; -const OriginalNextImage = NextImage.default; +import React from 'react'; -Object.defineProperty(NextImage, "default", { - configurable: true, - value: (props) => ( - - ), -}); +import { ThemeProvider } from 'styled-components'; +import GlobalStyle from '../src/styles/global'; +import theme from '../src/styles/theme'; +import { DocsContainer } from '@storybook/blocks'; +import '@devlaunchers/tailwind/tailwind.css'; +const isDevelopmentEnv = process.env.NODE_ENV == 'development'; const customViewports = { + mobile: { + name: 'mobile', + styles: { + width: `412px`, + height: '100%', + }, + }, sm: { name: 'sm', styles: { - width: `${theme.breakpoints.sm}px`, + width: `640px`, height: '100%', }, }, md: { name: 'md', styles: { - width: `${theme.breakpoints.md}px`, + width: `768px`, height: '100%', }, }, lg: { name: 'lg', styles: { - width: `${theme.breakpoints.lg}px`, + width: `1024px`, height: '100%', }, }, xl: { name: 'xl', styles: { - width: `${theme.breakpoints.xl}px`, + width: `1280px`, height: '100%', }, }, - xxl: { - name: 'xxl', + '2xl': { + name: '2xl', styles: { - width: `${theme.breakpoints.xxl}px`, + width: `1536px`, height: '100%', }, }, }; - + /* * Global decorator to apply the styles to all stories * Read more about them at: * https://storybook.js.org/docs/react/writing-stories/decorators#global-decorators */ export const decorators = [ - (Story) => ( - <> - + // apply global theme + (Story) => { + return ( + <> + - - - ), + + + ); + }, + // do Figma comparison in development mode + (Story, { parameters }) => { + if ( + process.env.STORYBOOK_FIGMA_ACCESS_TOKEN && + isDevelopmentEnv && + parameters.design + ) { + const figmaUrl = new URL(parameters.design.url); + return ( + + + + ); + } + return ; + }, ]; + +const DocsThemeContainer = ({ children, ...props }) => { + return ( + + + + {children} + + + ); +}; + export const parameters = { - nextRouter: { - Provider: RouterContext.Provider, - }, - actions: { argTypesRegex: "^on[A-Z].*" }, + actions: { argTypesRegex: '^on.*' }, controls: { matchers: { color: /(background|color)$/i, @@ -83,4 +114,7 @@ export const parameters = { viewport: { viewports: customViewports, }, + docs: { + container: DocsThemeContainer, + }, }; diff --git a/packages/UI/CONTRIBUTING.md b/packages/UI/CONTRIBUTING.md new file mode 100644 index 000000000..7827fbd78 --- /dev/null +++ b/packages/UI/CONTRIBUTING.md @@ -0,0 +1,178 @@ +# Getting started with the Platform Enablement Team + +dev-launchers-platform is a mono repo using yarn. All yarn script lines need to be run in the root folder. + +## Installing and quick started +1. install yarn, below are several ways to install. + - In a terminal with admin run ```$ corepack enable``` + - ```$ npm install --global yarn``` + +2. clone the repo @ https://github.com/dev-launchers/dev-launchers-platform +3. After cloning repo, navigate to /dev-launchers-platform +4. ```$ yarn install``` in the root folder to install dependencies +5. Open a new terminal and run ```$ yarn workspace @devlaunchers/tailwind dev```. to start tailwind constructor. +5. Open a 2nd terminal and run ```$ yarn workspace @devlaunchers/components storybook``` to start storybook. +7. Start Development work. + +### Yarn scripts for this team + +``` +$ yarn workspace @devlaunchers/tailwind dev +$ yarn workspace @devlaunchers/components storybook +$ yarn workspace @devlaunchers/components typecheck +``` +More scripts are located in the package.json file. + +### Installing packages + +To install a package into a specific workspace: + + $ yarn workspace @devlaunchers/ add + +#### Installing Radix-ui components + +Radix-ui components need to be installed individually, if you are not able to import @radix-ui follow the command below to install in devlaunchers components workspace. + + $ yarn workspace @devlaunchers/components add + +Below is the command to install radix-ui/react-checkbox + + $ yarn workspace @devlaunchers/components add @radix-ui/react-checkbox + + +# Boards and issues. + +### Issues in github + +When looking at issues in github, you will want to filter by Platform Enablement. + +https://github.com/dev-launchers/dev-launchers-platform/issues?q=is%3Aopen+is%3Aissue+label%3A%22Platform+Enablement%22 + +### Zenhub + +Platform Enablement Team uses zenhub as it's board. If this link does not work msg lead to invite you to the team. + +https://app.zenhub.com/workspaces/platform-enablement-63529f02029ee50018fe58c6/board + +## Git branch checkout flow + +We will be working out of development/components. + + $ git checkout development/components + +- after switching to the development/components branch, make sure you "yarn install" for new dependencies + +### Out of "development/components" branch all new branches will be created off of it. + + $ git checkout -b components/newBranchName + + +## Figma + +Go to universal Figma @: + + https://www.figma.com/file/EwzuhhvTulvFRMvhTD5VAh/DL-Universal-Design-System?type=design&t=w1PSPgQunxcd19Bz-6 + +- All component designs will be coming from the universal UI library. +- Make sure dev mode is toggled in Figma. +- All tailwind classes will be made up of dashes, other characters like '/' will be replaced with '-'. + +## Vscode extensions + +```extensions.json``` in this workspace will recommend the follow extensions when you first open the workspace: + +1. Tailwind css intellisense +2. Eslint +3. ES7 + React/redux/React-native snippets. +4. Figma for vs code + + + +### Storybook snippet + +By typing "story" in an empty file, you can get the below snippet as a starter for storybook files. When thes snippet is imported it will have selected all the parts needs to type in your Component. + +``` +import type { Meta, StoryObj } from '@storybook/react'; + +import Component from './Component'; + +const meta: Meta = { + component: Component, +}; + +export default meta; +type Story = StoryObj; + +/* + *👇 Render functions are a framework specific feature to allow you control on how the component renders. + * See https://storybook.js.org/docs/react/api/csf + * to learn how to use render functions. + */ +export const Primary: Story = { + render: () => , +}; +``` + +## Storybook & tailwind dev environment. + +In a terminal, in order to get tailwind classes in your project you will need to run the dev command. + + $ yarn workspace @devlaunchers/tailwind dev + +Storybook will be the primary way you will see your design changes. Running "components storybook" this is open up a browser for storybook. This is where you can see changes that are made. By running the command below, a window should open with storybook running. + + $ yarn workspace @devlaunchers/components storybook + + + +## Creating new components in project. + +When creating the folder/files in src/components : + +- The Project Components folder should match the Figma design location + - Example "src/components/Checkbox" +- Project Components names should match the design Name - "Capitalized" + - Example "src/components/Checkbox/Checkbox.tsx" +- Project components need a #.stories.tsx to work with storybook. + - find a template or copy from another stories.tsx +- Create an index.ts file for exporting the tsx files. + ``` + export { default as Alert } from './Alert'; + export type {AlertProp} from './Alert' + ``` + + +## Adding assets + +For .tsx assets check the need too re-export all components into an index.ts for exporting in the folder that it is living in. + +``` +packages/UI/src/assets/icons/index.ts +``` + + +## Jsdocs auto intergration with storybook. + +Attempt to put all documentation via jsdocs. Storybook will auto import depending on the location of the jsdocs. Make it so documenation is only in one location in code, and both storybook and jsdocs draw from the same location. + +As a example - Tabs story in storybook will grab this jsdocs, at the same time it will be shown by a description in mouse hover. + +![Example Image](example.png) + +## Submit the branch with a new pull request. + +In git bash, you will want to do the git push flow. + + - git add . + - Git commit -m "describes what you worked on" + - git push origin branch + +Go to github.com and make sure you are logged in and in the right repo. +https://github.com/dev-launchers/dev-launchers-platform + + - Go to "Pull Requests" tab. + - Create new "Pull request" + - the base branch is "development/components" + - the compare branch is your new branch + - Rest on technical lead to merge or give feedback. diff --git a/packages/UI/example.png b/packages/UI/example.png new file mode 100644 index 000000000..3271be235 Binary files /dev/null and b/packages/UI/example.png differ diff --git a/packages/UI/package.json b/packages/UI/package.json index b12f03e46..608c16b9a 100644 --- a/packages/UI/package.json +++ b/packages/UI/package.json @@ -4,32 +4,43 @@ "private": true, "dependencies": { "@devlaunchers/models": "workspace:*", + "@devlaunchers/tailwind": "workspace:*", "@devlaunchers/utility": "workspace:*", - "@storybook/addon-viewport": "6.5.16", + "@radix-ui/react-avatar": "1.0.4", + "@radix-ui/react-checkbox": "1.0.4", + "@radix-ui/react-dialog": "1.0.5", + "@radix-ui/react-dropdown-menu": "2.0.6", + "@radix-ui/react-switch": "1.0.3", + "@radix-ui/react-tabs": "1.0.4", "axios": "^0.27.2", "babel-loader": "8.2.5", + "clsx": "2.0.0", "constate": "^3.3.2", - "formik": "2.2.9", - "framer-motion": "8.4.3", + "formik": "^2.2.9", + "framer-motion": "6.5.1", + "iso8601-duration": "2.1.2", + "lucide-react": "0.314.0", "next": "^12.2.3", "polished": "^4.2.2", "react": "^17.0.2", "react-burger-menu": "^3.0.8", "react-dom": "^17.0.2", "react-scripts": "5.0.1", - "storybook-addon-next-router": "^4.0.0", "storybook-react-context": "^0.6.0", "styled-normalize": "^8.0.7", + "tailwind-merge": "^1.13.2", + "tailwind-variants": "0.1.13", + "uuid": "^8.3.2", "web-vitals": "^2.1.4", "zod": "3.19.1", "zod-formik-adapter": "1.1.1" }, "scripts": { - "storybook": "start-storybook -p 6006", - "build-storybook": "build-storybook", + "storybook": "storybook dev -p 6006", + "build-storybook": "storybook build", "clean": "rimraf --no-glob ./tsconfig.tsbuildinfo", - "lint": "eslint . --ext .ts,.tsx,.js,.jsx,.cjs,.mjs,.mdx", - "fix-all-files": "eslint . --ext .ts,.tsx,.js,.jsx,.cjs,.mjs,.mdx --fix", + "lint": "eslint . --ext .ts,.tsx,.js,.jsx,.cjs,.mjs", + "fix-all-files": "eslint . --ext .ts,.tsx,.js,.jsx,.cjs,.mjs --fix", "typecheck": "tsc --project ./tsconfig.json --noEmit", "test": "run-s 'test:*'", "test:unit": "echo \"No tests yet\"", @@ -55,15 +66,18 @@ }, "devDependencies": { "@devlaunchers/eslint-config-bases": "workspace:^", - "@storybook/addon-a11y": "6.5.10", - "@storybook/addon-actions": "^6.5.10", - "@storybook/addon-essentials": "^6.5.10", - "@storybook/addon-links": "^6.5.10", - "@storybook/jest": "0.0.10", - "@storybook/node-logger": "^6.5.10", - "@storybook/react": "^6.5.10", - "@storybook/test-runner": "0.5.0", - "@storybook/testing-library": "0.0.13", + "@storybook/addon-a11y": "7.2.2", + "@storybook/addon-actions": "^7.2.2", + "@storybook/addon-designs": "7.0.9", + "@storybook/addon-essentials": "^7.2.2", + "@storybook/addon-links": "^7.2.2", + "@storybook/addon-mdx-gfm": "7.2.2", + "@storybook/jest": "0.1.0", + "@storybook/nextjs": "7.2.2", + "@storybook/node-logger": "^7.2.2", + "@storybook/react": "^7.2.2", + "@storybook/test-runner": "0.13.0", + "@storybook/testing-library": "0.2.0", "@testing-library/jest-dom": "5.16.4", "@testing-library/react": "13.3.0", "@testing-library/react-hooks": "8.0.1", @@ -71,6 +85,7 @@ "@types/react": "^17.0.0", "@types/react-dom": "^17.0.15", "@types/styled-components": "5.1.25", + "@types/uuid": "9.0.6", "@typescript-eslint/eslint-plugin": "5.32.0", "@typescript-eslint/parser": "5.32.0", "axe-playwright": "1.1.11", @@ -92,8 +107,11 @@ "prettier": "^2.7.1", "rimraf": "3.0.2", "shell-quote": "1.7.3", + "storybook": "7.2.2", + "storybook-addon-pseudo-states": "2.1.2", "styled-components": "^5.2.1", "ts-jest": "28.0.7", + "type-fest": "4.3.1", "typescript": "^4.7.4" } } diff --git a/packages/UI/public/Person.svg b/packages/UI/public/Person.svg new file mode 100644 index 000000000..fe620baeb --- /dev/null +++ b/packages/UI/public/Person.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/packages/UI/src/assets/icons/Checkmark.tsx b/packages/UI/src/assets/icons/Checkmark.tsx new file mode 100644 index 000000000..bdc3a9126 --- /dev/null +++ b/packages/UI/src/assets/icons/Checkmark.tsx @@ -0,0 +1,24 @@ +import * as React from 'react'; + +function Checkmark(props: React.SVGProps) { + return ( + + + + ); +} + +export default Checkmark; diff --git a/packages/UI/src/assets/icons/Hamburger.tsx b/packages/UI/src/assets/icons/Hamburger.tsx new file mode 100644 index 000000000..b13d70447 --- /dev/null +++ b/packages/UI/src/assets/icons/Hamburger.tsx @@ -0,0 +1,24 @@ +import * as React from 'react'; + +function Hamburger(props: React.SVGProps) { + return ( + + + + ); +} + +export default Hamburger; diff --git a/packages/UI/src/assets/icons/index.ts b/packages/UI/src/assets/icons/index.ts index 2aaea75a4..bce810757 100644 --- a/packages/UI/src/assets/icons/index.ts +++ b/packages/UI/src/assets/icons/index.ts @@ -12,3 +12,4 @@ export { default as Github } from './Github'; export { default as Discord } from './Discord'; export { default as Link } from './Link'; export { default as Mail } from './Mail'; +export { default as Checkmark } from './Checkmark'; diff --git a/packages/UI/src/components/Checkbox/Checkbox.mdx b/packages/UI/src/components/Checkbox/Checkbox.mdx new file mode 100644 index 000000000..a6fb8c327 --- /dev/null +++ b/packages/UI/src/components/Checkbox/Checkbox.mdx @@ -0,0 +1,51 @@ +import { Meta, Controls, Stories,Description, Primary } from '@storybook/blocks'; +import * as CheckboxStories from './Checkbox.stories.tsx'; +import Checkbox from './Checkbox.tsx'; + + + + + +
      +
      +

      Default

      +

      Hover

      +

      Focus

      +

      Disabled

      +
      +
      + + + + + + + + + + {/* {YOU CAN ALSO USE storybook-addon-designs } */} +