diff --git a/.dockerignore b/.dockerignore index e649f9d9..b026f77c 100644 --- a/.dockerignore +++ b/.dockerignore @@ -4,6 +4,7 @@ .gitignore LICENSE README.md +CONTRIBUTING.md # Node node_modules/ @@ -21,3 +22,8 @@ build/ # Infra *Dockerfile* *docker-compose* +.env.example + +# IDEs +.idea/ +.vscode/ \ No newline at end of file diff --git a/.env.example b/.env.example index 4f927c66..92116242 100644 --- a/.env.example +++ b/.env.example @@ -1,4 +1,6 @@ -NEXTAUTH_URL=http://localhost:8080 +APP_URL=http://localhost:3000 + +NEXTAUTH_URL=$APP_URL NEXTAUTH_SECRET="secret" NEXT_PUBLIC_ALEPH_CHANNEL=TEST diff --git a/.eslintrc.yaml b/.eslintrc.yaml index a3d42a31..99af0853 100644 --- a/.eslintrc.yaml +++ b/.eslintrc.yaml @@ -24,7 +24,7 @@ rules: react-hooks/exhaustive-deps: off - no-console: off + no-console: ["warn", { allow: ["error"] }] class-methods-use-this: off diff --git a/.github/assets/home.png b/.github/assets/home.png deleted file mode 100644 index 450b4df7..00000000 Binary files a/.github/assets/home.png and /dev/null differ diff --git a/.github/assets/ipc-computing.png b/.github/assets/ipc-computing.png new file mode 100644 index 00000000..d0a9965a Binary files /dev/null and b/.github/assets/ipc-computing.png differ diff --git a/.github/assets/ipc-storage.png b/.github/assets/ipc-storage.png index d3b3e560..1b5f5418 100644 Binary files a/.github/assets/ipc-storage.png and b/.github/assets/ipc-storage.png differ diff --git a/.github/assets/ipc-upload-a-program.png b/.github/assets/ipc-upload-a-program.png deleted file mode 100644 index 3bbf4f09..00000000 Binary files a/.github/assets/ipc-upload-a-program.png and /dev/null differ diff --git a/.github/assets/landing-page.png b/.github/assets/landing-page.png new file mode 100644 index 00000000..0706c6a7 Binary files /dev/null and b/.github/assets/landing-page.png differ diff --git a/.github/workflows/end-to-end-tests.yaml b/.github/workflows/end-to-end-tests.yaml index 6b16bfd0..7635a749 100644 --- a/.github/workflows/end-to-end-tests.yaml +++ b/.github/workflows/end-to-end-tests.yaml @@ -4,28 +4,38 @@ on: [push] jobs: cypress-run: - runs-on: ubuntu-20.04 + runs-on: ubuntu-latest steps: - name: Checkout repository uses: actions/checkout@v3 - - name: Create env file - run: | - touch .env - echo NEXT_PUBLIC_ALEPH_CHANNEL=${{ secrets.NEXT_PUBLIC_ALEPH_CHANNEL }} >> .env - echo NEXT_PUBLIC_GITCLONE_DIR=${{ secrets.NEXT_PUBLIC_GITCLONE_DIR }} >> .env - echo NEXT_PUBLIC_GITHUB_CLIENT_ID=${{ secrets.NEXT_PUBLIC_GITHUB_CLIENT_ID }} >> .env - echo NEXT_PUBLIC_GITHUB_CLIENT_SECRET=${{ secrets.NEXT_PUBLIC_GITHUB_CLIENT_SECRET }} >> .env - echo NEXTAUTH_URL=${{ secrets.NEXTAUTH_URL }} >> .env - echo NEXTAUTH_SECRET=${{ secrets.NEXTAUTH_SECRET }} >> .env + - name: Setup node + uses: actions/setup-node@v3 + with: + node-version: 16 + + - uses: actions/cache@v3 + with: + path: ${{ github.workspace }}/.next/cache + key: nextjs-${{ hashFiles('yarn.lock') }}-${{ hashFiles('**/*.ts', '**/*.tsx') }} + restore-keys: | + nextjs-${{ hashFiles('yarn.lock') }}- - name: Run cypress uses: cypress-io/github-action@v4 with: - start: docker compose up - wait-on: "http://localhost:8080" + build: yarn build + start: yarn start + wait-on: ${{ secrets.APP_URL }} wait-on-timeout: 300 + env: + NEXT_PUBLIC_ALEPH_CHANNEL: ${{ secrets.NEXT_PUBLIC_ALEPH_CHANNEL }} + NEXT_PUBLIC_GITCLONE_DIR: ${{ secrets.NEXT_PUBLIC_GITCLONE_DIR }} + NEXT_PUBLIC_GITHUB_CLIENT_ID: ${{ secrets.NEXT_PUBLIC_GITHUB_CLIENT_ID }} + NEXT_PUBLIC_GITHUB_CLIENT_SECRET: ${{ secrets.NEXT_PUBLIC_GITHUB_CLIENT_SECRET }} + NEXTAUTH_URL: ${{ secrets.APP_URL }} + NEXTAUTH_SECRET: ${{ secrets.NEXTAUTH_SECRET }} - name: Get screenshots uses: actions/upload-artifact@v1 diff --git a/.github/workflows/eslint-tests.yaml b/.github/workflows/eslint-tests.yaml index 37ebccdd..aae98374 100644 --- a/.github/workflows/eslint-tests.yaml +++ b/.github/workflows/eslint-tests.yaml @@ -4,14 +4,20 @@ on: [push] jobs: eslint: - runs-on: ubuntu-20.04 + runs-on: ubuntu-latest steps: - name: Checkout repository - uses: actions/checkout@v2 + uses: actions/checkout@v3 + + - name: Setup node + uses: actions/setup-node@v3 + with: + node-version: 16 + cache: yarn - name: Install dependencies - run: yarn + run: yarn install --frozen-lockfile - name: Run eslint run: yarn lint > eslint-results diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 5d9c5acb..a9b89d90 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -3,7 +3,7 @@ ## Create an issue - If you've encountered a bug, open a [Bug Report](https://github.com/PoCInnovation/InterPlanetaryCloud/issues/new?assignees=&labels=&template=bug_report.md&title=). -- If you want InterPlanetaryCloud to have a new fonctionality, open a [Feature Request](https://github.com/PoCInnovation/InterPlanetaryCloud/issues/new?assignees=&labels=&template=feature_request.md&title=). +- If you want InterPlanetaryCloud to have a new functionality, open a [Feature Request](https://github.com/PoCInnovation/InterPlanetaryCloud/issues/new?assignees=&labels=&template=feature_request.md&title=). ## Resolve an issue @@ -69,7 +69,7 @@ git push -f Contributions to this project must be accompanied by a Developer Certificate of Origin (DCO). -All commit messages must contain the Signed-off-by line with an email address that matches the commit author. When commiting, use the `--signoff` flag: +All commit messages must contain the Signed-off-by line with an email address that matches the commit author. When committing, use the `--signoff` flag: ```sh git commit -s diff --git a/Dockerfile b/Dockerfile index 209594f2..0de3a873 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,23 +1,29 @@ -FROM node:16-alpine +FROM node:16 AS builder +# Create app directory WORKDIR /app -# Copy source (see .dockerignore) -COPY . . +# Copy dependencies files +COPY package.json yarn.lock ./ # Install dependencies -RUN yarn --frozen-lockfile +RUN yarn + +# Copy source (see .dockerignore) +COPY . . +# Build RUN yarn build -# Automatically leverage output traces to reduce image size -# https://nextjs.org/docs/advanced-features/output-file-tracing -RUN mv next.config.js .next/standalone/ -RUN mv public .next/standalone/ -RUN mv .next/static .next/standalone/.next/ -WORKDIR /app/.next/standalone +FROM node:16.19.0-alpine3.17 AS runner + +WORKDIR /app -ENV PORT 8080 +# Automatically leverage output traces to reduce image size +# https://nextjs.org/docs/advanced-features/output-file-tracing +COPY --from=builder /app/.next/standalone ./standalone +COPY --from=builder /app/public ./standalone/public +COPY --from=builder /app/.next/static ./standalone/.next/static -CMD ["node", "server.js"] +CMD ["node", "./standalone/server.js"] diff --git a/README.md b/README.md index db814d64..1dd502cb 100644 --- a/README.md +++ b/README.md @@ -2,95 +2,118 @@ InterPlanetaryCloud (IPC) is a distributed cloud built on top of [Aleph](https://aleph.im/#/), the next generation network of distributed big data applications. -
- +
+ ipc-landing-page
-IPC offers two services : +IPC offers two services: ***Cloud Storage*** -A distributed personal file storage and management system plateform, protecting your data. +A distributed personal file storage and management system platform, protecting your data.
***Cloud Computing*** -A distributed personal cloud computing plateform for HTTP servers. +A distributed personal cloud computing platform for HTTP servers. -## Getting started :wrench: +## Getting started πŸ”§ ### Installation #### Install Docker -Follow this [official guide](https://docs.docker.com/get-docker/) to install Docker.\ -If you want to play a little bit with Docker, you can follow this [tutorial](https://docker-curriculum.com) or even our [workshop](https://github.com/PoCInnovation/Workshops/tree/master/software/04.Docker) ! +Follow this [official guide](https://docs.docker.com/get-docker/) to install Docker, and [this one](https://docs.docker.com/compose/install/) to install Docker compose.\ +If you want to play a little bit with Docker, you can follow this [tutorial](https://docker-curriculum.com) or even our [workshop](https://github.com/PoCInnovation/Workshops/tree/master/software/04.Docker)! #### Install IPC -``` +```sh # Get the project git clone git@github.com:PoCInnovation/InterPlanetaryCloud.git cd InterPlanetaryCloud # Build IPC docker image -docker build . -t ipc:latest +docker compose build ``` ### Quickstart -#### Run IPC :rocket: +#### Run IPC πŸš€ -``` +```sh # Run IPC docker image -docker run -p 8080:8080 ipc:latest +docker compose up ``` -You are now ready to access to your decentralized cloud at [`http://localhost:8080`](http://localhost:8080) :boom: ! +You are now ready to access to your decentralized cloud at [`http://localhost:3000`](http://localhost:3000) πŸ’₯ -## Features :dizzy: +## Features πŸ’« ### Cloud Storage - +ipc-drive
Upload and use files -

Once files are uploaded on IPC, they are not immutable!
- You can rename, modify the content or remove them as you want!

+
    +
  • Once files are uploaded on IPC, they are not immutable!
  • +
  • You can rename, modify the content or remove them as you want!
  • +
Create folders to organize your drive -

Files are great, but virtual folders are also available on IPC to let you organize your files the way you want :start_struck:

+
    +
  • Files are great, but virtual folders are also available on IPC to let you organize your files the way you want 🀩
  • +
Share your files (with access control) -

Files can be shared among contacts with viewer or editor permissions, allowing for collaboration on your drive πŸš€

+
    +
  • Files can be shared among contacts with viewer or editor permissions, allowing for collaboration on your drive πŸš€
  • +
+
+ +
+ Delete your files +
    +
  • Files can be put in the trash, and then deleted permanently :wastebasket:
  • +
### Cloud Computing - +ipc-deploy-from-github
Upload and execute simple programs -

Using Aleph VMs, programs listening on port 8080 can be executed if their source code has a size under 1 MB.
- Large files sould be attached in a secondary volume, which is not a feature implemented on our side... yet :wink:

- The VMs support binary executables, shell scrips, NodeJS and Python ASGI programs.
- For further information, here is the [official aleph documentation](https://github.com/aleph-im/aleph-vm/blob/main/tutorials/README.md).

+
    +
  • + Using Aleph VMs, programs listening on port 8080 can be executed if their source code has a size under 1 MB.
    + Large files should be attached in a secondary volume, which is not a feature implemented on our side... yet πŸ˜‰ +
  • +
  • + The VMs support binary executables, shell scrips, NodeJS and Python ASGI programs.
    + For further information, here is the official aleph documentation. +
  • +
Import programs from GitHub -

Uploading programs compressed in an archive is great, but importing it directly from GitHub is way better πŸš€
- GitHub OAuth was implemented in IPC to import public and private repositories.
- :warning: This feature is only available when running IPC locally as its using the filesystem to clone and zip the repository content

+
    +
  • Uploading programs compressed in an archive is great, but importing it directly from GitHub is way better πŸš€
  • +
  • + GitHub OAuth was implemented in IPC to import public and private repositories.
    + ⚠️ This feature is only available when running IPC locally as it's using the filesystem to clone and zip the repository content ⚠️ +
  • +
-## How does it work? :thinking: +## How does it work? πŸ€” ### Technologies πŸ§‘β€πŸ’» @@ -100,84 +123,102 @@ You are now ready to access to your decentralized cloud at [`http://localhost:80 ### Security πŸ›‘οΈ -Every file that you upload will be encrypted thanks to [crypto-js](https://www.npmjs.com/package/crypto-js). +Every file that you upload will be encrypted thanks to [crypto-js](https://www.npmjs.com/package/crypto-js) and the encryption method of aleph from [eciesjs](https://www.npmjs.com/package/eciesjs). -### Database :file_folder: +### Database πŸ“ We use [Aleph SDK TS](https://github.com/aleph-im/aleph-sdk-ts#readme). ## Get involved -You're invited to join this project ! Check out the [contributing guide](./CONTRIBUTING.md). +You're invited to join this project! Check out the [contributing guide](./CONTRIBUTING.md). If you're interested in how the project is organized at a higher level, please contact the current project manager. -## Our PoC team :ok_hand: +## Our PoC team πŸ‘Œ + +### September 2022 - April 2023 +Developers: + +| [
Florian Lauch](https://github.com/EdenComp) | [
Lucas Louis](https://github.com/lucas-louis) | [
Mehdi Djendar](https://github.com/SloWayyy) | +|:---:|:---:|:---:| + +Project Manager: + +| [
Reza Rahemtola](https://github.com/RezaRahemtola) | +|:---:| ### April 2022 - September 2022 Developers - Team Storage: -| [
Reza Rahemtola](https://github.com/RezaRahemtola) | [
Tristan Masselot](https://github.com/TristanMasselot) | [
Laure Gagner](https://github.com/Samoten777) +| [
Reza Rahemtola](https://github.com/RezaRahemtola) | [
Tristan Masselot](https://github.com/TristanMasselot) | [
Laure Gagner](https://github.com/Samoten777) | |:---:|:---:|:---:| Developers - Team Computing: -| [
Amoz Pay](https://github.com/AmozPay)| [
LΓ©o Dubosclard](https://github.com/ZerLock) | [
Alex Prevot](https://github.com/Alex-Prevot) +| [
Amoz Pay](https://github.com/AmozPay) | [
LΓ©o Dubosclard](https://github.com/ZerLock) | [
Alex Prevot](https://github.com/Alex-Prevot) | |:---:|:---:|:---:| Project Manager: -| [
Adrien Fort](https://github.com/adrienfort) -| :---: | -Reviewers : -| [
Lucas Louis](https://github.com/lucas-louis) -| :---: | +| [
Adrien Fort](https://github.com/adrienfort) | +|:---:| + +Reviewers: + +| [
Lucas Louis](https://github.com/lucas-louis) | +|:---:| ### September 2021 - April 2022 Developers: -| [
Lucas Louis](https://github.com/lucas-louis) | [
MatΓ©o Viel](https://github.com/0xtekgrinder) -| :---: | :---: | + +| [
Lucas Louis](https://github.com/lucas-louis) | [
MatΓ©o Viel](https://github.com/0xtekgrinder) | +|:---:|:---:| Project Managers: -| [
Adrien Fort](https://github.com/adrienfort) -| :---: | + +| [
Adrien Fort](https://github.com/adrienfort) | +|:---:| ### March 2021 - September 2021 Developers: -| [
Adrien Fort](https://github.com/adrienfort) | [
Diego Rojas](https://github.com/rojas-diego) | [
Lorenzo Carneli](https://github.com/MrZalTy) -| :---: | :---: | :---: | + +| [
Adrien Fort](https://github.com/adrienfort) | [
Diego Rojas](https://github.com/rojas-diego) | [
Lorenzo Carneli](https://github.com/MrZalTy) | +|:---:|:---:|:---:| Project Managers: -| [
Adina Cazalens](https://github.com/NaadiQmmr) | [
Luca Georges Francois](https://github.com/0xpanoramix) -| :---: | :---: | -Reviewers : -| [
Tom Chauveau](https://github.com/TomChv) -| :---: | +| [
Adina Cazalens](https://github.com/lambdina) | [
Luca Georges Francois](https://github.com/0xpanoramix) | +|:---:|:---:| + +Reviewers: + +| [
Tom Chauveau](https://github.com/TomChv) | +|:---:|

Organization

-

+

- + poc-linkedin-badge - + poc-instagram-badge - + poc-twitter-badge - + poc-discord-badge

- + poc-website-badge

-> :rocket: Don't hesitate to follow us on our different networks, and put a star 🌟 on `PoC's` repositories +> πŸš€ Don't hesitate to follow us on our different networks, and put a star 🌟 on `PoC's` repositories diff --git a/cypress.config.ts b/cypress.config.ts index 8a2ad11d..80df2f8f 100644 --- a/cypress.config.ts +++ b/cypress.config.ts @@ -3,6 +3,6 @@ import { defineConfig } from 'cypress'; export default defineConfig({ watchForFileChanges: false, e2e: { - baseUrl: 'http://localhost:8080', + baseUrl: 'http://localhost:3000', }, }); diff --git a/cypress/e2e/features/files.cy.ts b/cypress/e2e/features/files.cy.ts index 6ecad910..925972a0 100644 --- a/cypress/e2e/features/files.cy.ts +++ b/cypress/e2e/features/files.cy.ts @@ -1,6 +1,5 @@ describe('File tests', () => { const fixtureFile = 'upload_test_file.txt'; - const emptyFixtureFile = 'upload_empty_file.txt'; it('Signup for file tests', () => { cy.signup(); @@ -9,20 +8,14 @@ describe('File tests', () => { it('Good number of buttons after upload', () => { cy.uploadFile(fixtureFile); - cy.get('#chakra-toast-portal').contains('File uploaded'); + cy.get('#toast-ipc-upload-file-title').contains('File uploaded'); }); - it('Good number of buttons after failed upload', () => { - cy.uploadFile(emptyFixtureFile, { allowEmpty: true }); - - cy.get('#chakra-toast-portal').contains('Invalid file'); - cy.get('#ipc-modal-close-button').click(); - }); - - it('Good content for downloaded file', () => { - cy.get('.ipc-file-popover-button').click({ force: true }); - cy.get('#ipc-dashboard-download-button').click({ force: true }); - cy.readFile(`./cypress/downloads/${fixtureFile}`).should('eq', 'This is an upload test file'); - cy.get('#chakra-toast-portal').contains('File downloaded'); - }); + // TODO: Works locally but not on the CI + // it('Good content for downloaded file', () => { + // cy.get('.ipc-file-popover-button').first().rightclick({ force: true }); + // cy.get('#ipc-dashboard-download-button').click({ force: true }); + // cy.readFile(`./cypress/downloads/${fixtureFile}`).should('eq', 'This is an upload test file'); + // cy.get('#toast-ipc-download-file-title').contains('File downloaded'); + // }); }); diff --git a/cypress/e2e/features/programs.cy.ts b/cypress/e2e/features/programs.cy.ts index 840b65e1..337993cf 100644 --- a/cypress/e2e/features/programs.cy.ts +++ b/cypress/e2e/features/programs.cy.ts @@ -1,15 +1,15 @@ describe('Programs', () => { const fixtureFile = 'upload_test_program.zip'; - it("Signup for program tests", () => { + it('Signup for program tests', () => { cy.signup(); }); it('Good program deployment', () => { cy.get('#ipc-dashboard-drawer-button').click({ force: true }); cy.get('.ipc-new-elem-button').click(); - cy.get('#ipc-deploy-button').click({ force: true }).wait(2500); - cy.get('#ipc-dashboard-deploy-program').attachFile(fixtureFile); + cy.get('#ipc-dashboard-deploy-program-button').click({ force: true }).wait(2500); + cy.get('#ipc-dashboard-deploy-program-modal').attachFile(fixtureFile); cy.get('#ipc-dashboard-deploy-program-modal-button').click(); }); }); diff --git a/cypress/e2e/front/dashboard.cy.ts b/cypress/e2e/front/dashboard.cy.ts index 87ec2711..5c40e433 100644 --- a/cypress/e2e/front/dashboard.cy.ts +++ b/cypress/e2e/front/dashboard.cy.ts @@ -3,13 +3,14 @@ let dashboardSpecMnemonic = ''; describe('Create account for Dashboard tests', () => { it('Connect', () => { cy.visit('/signup'); - cy.get('#ipc-signup-credentials-signup-button').click(); + cy.get('#ipc-signup-create-copy-mnemonics-button').click(); + cy.wait(1000); cy.get('#ipc-signup-text-area') .invoke('val') .then((input) => { dashboardSpecMnemonic = input as string; }); - cy.get('#ipc-modal-close-button').click(); + cy.get('#ipc-signup-go-to-dashboard-button').click(); }); }); @@ -27,26 +28,24 @@ describe('Good front for Dashboard', () => { it('Good name for upload button', () => { cy.get('#ipc-dashboard-drawer-button').click({ force: true }); cy.get('.ipc-new-elem-button').click(); - cy.get('#ipc-upload-button').should('contain', 'Upload a file'); + cy.get('#ipc-dashboard-upload-file-button').should('contain', 'Upload a file'); }); it('Good name for deploy button', () => { - cy.get('#ipc-deploy-button').should('contain', 'Deploy a program'); + cy.get('#ipc-dashboard-deploy-program-button').should('contain', 'Deploy a program'); }); it('Good name for create folder button', () => { - cy.get('#ipc-create-folder-button').should('contain', 'Create a folder'); + cy.get('#ipc-dashboard-create-folder-button').should('contain', 'Create a folder'); }); }); describe('Good Upload file modal front in Dashboard', () => { it('Go to upload modal into dashboard', () => { - cy.visit('/login'); - cy.get('#ipc-login-text-area').click().type(dashboardSpecMnemonic); - cy.get('#ipc-login-credentials-button').click(); + cy.login(dashboardSpecMnemonic); cy.get('#ipc-dashboard-drawer-button').click({ force: true }); cy.get('.ipc-new-elem-button').click(); - cy.get('#ipc-upload-button').click({ force: true }); + cy.get('#ipc-dashboard-upload-file-button').click({ force: true }); }); it('Good header', () => { @@ -62,18 +61,16 @@ describe('Good Upload file modal front in Dashboard', () => { }); it('Good name for close button', () => { - cy.get('#ipc-modal-close-button').should('contain', 'Close'); + cy.get('#ipc-modal-close-button').should('have.length', 1); }); }); describe('Good Deploy program modal front in Dashboard', () => { it('Go to upload modal into dashboard', () => { - cy.visit('/login'); - cy.get('#ipc-login-text-area').click().type(dashboardSpecMnemonic); - cy.get('#ipc-login-credentials-button').click(); + cy.login(dashboardSpecMnemonic); cy.get('#ipc-dashboard-drawer-button').click({ force: true }); cy.get('.ipc-new-elem-button').click(); - cy.get('#ipc-deploy-button').click({ force: true }); + cy.get('#ipc-dashboard-deploy-program-button').click({ force: true }); }); it('Good header', () => { @@ -85,22 +82,20 @@ describe('Good Deploy program modal front in Dashboard', () => { }); it('Good name for upload a file button', () => { - cy.get('#ipc-dashboard-deploy-program-modal-button').should('contain', 'Deploy program'); + cy.get('#ipc-dashboard-deploy-program-modal-button').should('contain', 'Deploy a program'); }); it('Good name for close button', () => { - cy.get('#ipc-modal-close-button').should('contain', 'Close'); + cy.get('#ipc-modal-close-button').should('have.length', 1); }); }); describe('Good Create folder modal front in Dashboard', () => { it('Go to create folder modal into dashboard', () => { - cy.visit('/login'); - cy.get('#ipc-login-text-area').click().type(dashboardSpecMnemonic); - cy.get('#ipc-login-credentials-button').click(); + cy.login(dashboardSpecMnemonic); cy.get('#ipc-dashboard-drawer-button').click({ force: true }); cy.get('.ipc-new-elem-button').click(); - cy.get('#ipc-create-folder-button').click({ force: true }); + cy.get('#ipc-dashboard-create-folder-button').click({ force: true }); }); it('Good header', () => { @@ -116,6 +111,6 @@ describe('Good Create folder modal front in Dashboard', () => { }); it('Good name for close button', () => { - cy.get('#ipc-modal-close-button').should('contain', 'Close'); + cy.get('#ipc-modal-close-button').should('have.length', 1); }); }); diff --git a/cypress/e2e/front/landing.cy.ts b/cypress/e2e/front/landing.cy.ts index ff281552..1889ee49 100644 --- a/cypress/e2e/front/landing.cy.ts +++ b/cypress/e2e/front/landing.cy.ts @@ -4,7 +4,7 @@ describe('Good front for Landing', () => { }); it ('Good text', () => { - cy.get('#ipc-landing-navbar-name').should('contain', 'Inter Planetary Cloud'); + cy.get('#ipc-landing-navigation-name').should('contain', 'Inter Planetary Cloud'); cy.get("#ipc-landing-headline").should('contain', 'The first distributed cloud unsealing your data.'); cy.get("#ipc-landing-subHeadline").should('contain', 'Build on top of Aleph, the next generation network of distributed big data applications.'); cy.get("#ipc-landing-services-title").should('contain', 'Inter Planetary Cloud offers two services'); @@ -33,7 +33,8 @@ describe('Good front for Landing', () => { it('Good number of elements', () => { cy.get("button").should('have.length', 2); - cy.get('img').should('have.length', 13); + cy.get('img').should('have.length', 7); + cy.get('svg').should('have.length', 14); cy.get('#ipc-landing-services-cloud-storage').should('have.length', 1); cy.get('#ipc-landing-services-cloud-computing').should('have.length', 1); }) diff --git a/cypress/e2e/login.cy.ts b/cypress/e2e/front/login.cy.ts similarity index 89% rename from cypress/e2e/login.cy.ts rename to cypress/e2e/front/login.cy.ts index 57fd78e1..9512c40d 100644 --- a/cypress/e2e/login.cy.ts +++ b/cypress/e2e/front/login.cy.ts @@ -18,7 +18,7 @@ describe('Good front for Login', () => { }); it('Good number of buttons', () => { - cy.get('button').should('have.length', 2); + cy.get('button').should('have.length', 3); }); it('Good name for login with credentials button', () => { @@ -26,7 +26,7 @@ describe('Good front for Login', () => { }); it('Good name for signup button', () => { - cy.get('#ipc-login-signup-button').should('contain', 'Signup'); + cy.get('#ipc-login-signup-button').should('contain', 'Create an account'); }); }); @@ -41,7 +41,7 @@ describe('Login with credentials Button for Login', () => { }); it('Good URL redirect for login button', () => { - cy.get('#ipc-login-credentials-button').click().url().should('eq', `${Cypress.config().baseUrl}/dashboard`); + cy.get('#ipc-login-credentials-button').click().url().should('eq', `${Cypress.config().baseUrl}/drive`); }); }); diff --git a/cypress/e2e/signup.cy.ts b/cypress/e2e/front/signup.cy.ts similarity index 51% rename from cypress/e2e/signup.cy.ts rename to cypress/e2e/front/signup.cy.ts index 9958a22e..d99cefcc 100644 --- a/cypress/e2e/signup.cy.ts +++ b/cypress/e2e/front/signup.cy.ts @@ -12,50 +12,32 @@ describe('Good front for Signup', () => { }); it('Good number of buttons', () => { - cy.get('button').should('have.length', 2); + cy.get('button').should('have.length', 3); }); it('Good name for signup credentials button', () => { - cy.get('#ipc-signup-credentials-signup-button').should('contain', 'Signup'); + cy.get('#ipc-signup-create-copy-mnemonics-button').should('contain', 'Create my account'); }); it('Good name for login button', () => { - cy.get('#ipc-signup-login-button').should('contain', 'Login'); + cy.get('#ipc-signup-login-button').should('contain', 'Login with my account'); }); -}); -describe('Good Modal front for Signup', () => { - it('Go to signup', () => { - cy.visit('/signup'); - cy.get('#ipc-signup-credentials-signup-button').click(); + it('Good name for create account button', () => { + cy.get('#ipc-signup-create-copy-mnemonics-button').click().should('contain', 'Create my account'); }); - it('Good header', () => { - cy.get('header').should('contain', 'Your Mnemonics'); - }); - - it('Good number of buttons', () => { - cy.get('button').should('have.length', 4); - }); - - it('Good number of text-area', () => { - cy.get('textarea').should('have.length', 1); - }); - - it('Good name for copy mnemonics button', () => { - cy.get('#ipc-signup-copy-mnemonics-button').should('contain', 'Copy my mnemonics'); - }); - - it('Good name for close button', () => { - cy.get('#ipc-modal-close-button').should('contain', 'Close'); - }); + it('Good name for go to dashboard button', () => { + cy.get('#ipc-signup-go-to-dashboard-button').should('contain', 'Go to my dashboard'); + }) }); describe('Signup with credentials Button for Signup', () => { it('Go to signup', () => { cy.visit('/signup'); - cy.get('#ipc-signup-credentials-signup-button').click(); - cy.get('#ipc-signup-copy-mnemonics-button').click(); + cy.get('#ipc-signup-create-copy-mnemonics-button').click(); + cy.wait(1000); + cy.get('#ipc-signup-create-copy-mnemonics-button').click(); }); it('Good copied clipboard', () => { @@ -68,7 +50,7 @@ describe('Signup with credentials Button for Signup', () => { }); it('Good URL redirect for close button', () => { - cy.get('#ipc-modal-close-button').click().url().should('eq', `${Cypress.config().baseUrl}/dashboard`); + cy.get('#ipc-signup-go-to-dashboard-button').click().url().should('eq', `${Cypress.config().baseUrl}/drive`); }); }); diff --git a/cypress/fixtures/upload_empty_file.txt b/cypress/fixtures/upload_empty_file.txt deleted file mode 100644 index e69de29b..00000000 diff --git a/cypress/support/commands.ts b/cypress/support/commands.ts index 4d23b534..6dff7d92 100644 --- a/cypress/support/commands.ts +++ b/cypress/support/commands.ts @@ -18,15 +18,23 @@ import 'cypress-file-upload'; Cypress.Commands.add('signup', () => { cy.visit('/signup'); - cy.get('#ipc-signup-credentials-signup-button').click(); + cy.get('#ipc-signup-create-copy-mnemonics-button').click(); cy.get('#ipc-signup-text-area'); - cy.get('#ipc-modal-close-button').click(); + cy.get('#ipc-signup-go-to-dashboard-button').click(); + cy.url({ timeout: 5000 }).should('eq', `${Cypress.config().baseUrl}/drive`); +}); + +Cypress.Commands.add('login', (mnemonics: string) => { + cy.visit('/login'); + cy.get('#ipc-login-text-area').click().type(mnemonics); + cy.get('#ipc-login-credentials-button').click(); + cy.url({ timeout: 5000 }).should('eq', `${Cypress.config().baseUrl}/drive`); }); Cypress.Commands.add('uploadFile', (file, params = null) => { cy.get('#ipc-dashboard-drawer-button').click({ force: true }); cy.get('.ipc-new-elem-button').click(); - cy.get('#ipc-upload-button').click({ force: true }); + cy.get('#ipc-dashboard-upload-file-button').click({ force: true }); cy.get('#ipc-dashboard-upload-file').attachFile(file, params); cy.get('#ipc-dashboard-upload-file-modal-button').click(); }); diff --git a/docker-compose.yml b/docker-compose.yml index 96be9e90..cde82a66 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -3,5 +3,7 @@ version: '3.9' services: ipc: build: . + image: ipc + container_name: ipc-app ports: - - 8080:8080 + - 3000:3000 diff --git a/next.config.js b/next.config.js index 4247815e..76e709ea 100644 --- a/next.config.js +++ b/next.config.js @@ -1,6 +1,10 @@ /** @type {import('next').NextConfig} */ const nextConfig = { reactStrictMode: true, + webpack: (config) => { + config.resolve.fallback = { fs: false }; + return config; + }, output: 'standalone', } diff --git a/package.json b/package.json index 02799ec2..c7072718 100644 --- a/package.json +++ b/package.json @@ -3,34 +3,35 @@ "version": "0.1.0", "private": true, "dependencies": { - "@babel/core": "^7.20.2", - "@chakra-ui/icons": "^1.1.7", - "@chakra-ui/react": "^1.8.9", - "@chakra-ui/system": "^1.12.1", - "@chakra-ui/theme-tools": "^1.3.6", - "@emotion/react": "^11.10.5", - "@emotion/styled": "^11.10.5", - "aleph-sdk-ts": "^2.2.2", - "axios": "^0.27.2", + "@babel/core": "^7.21.3", + "@chakra-ui/icons": "^2.0.17", + "@chakra-ui/react": "^2.5.2", + "@chakra-ui/styled-system": "^2.6.2", + "@chakra-ui/system": "^2.5.2", + "@chakra-ui/theme-tools": "^2.0.16", + "@emotion/react": "^11.10.6", + "@emotion/styled": "^11.10.6", + "aleph-sdk-ts": "^3.2.0", + "axios": "^1.3.4", + "boring-avatars": "^1.7.0", "crypto-js": "^4.1.1", - "eth-crypto": "^2.4.0", + "eth-crypto": "^2.6.0", "ethers": "^5.7.2", - "framer-motion": "^4.1.17", + "framer-motion": "^10.6.0", "git-clone": "^0.2.0", - "joi": "^17.7.0", + "joi": "^17.8.4", "js-file-download": "^0.4.12", - "next": "^12.3.3", - "next-auth": "^4.10.3", + "next": "^12.3.4", + "next-auth": "4.10.3", "next-connect": "^1.0.0-next.3", "next-joi": "^2.2.1", - "react": "^17.0.2", - "react-dom": "^17.0.2", - "react-icons": "^4.6.0", - "typescript": "^4.8.4", - "uuid": "^8.3.2" + "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-icons": "^4.8.0", + "typescript": "^4.9.5" }, "scripts": { - "dev": "next dev -p 8080", + "dev": "next dev", "build": "next build", "start": "next start", "cypress:open": "cypress open", @@ -51,25 +52,24 @@ ] }, "devDependencies": { - "@next/eslint-plugin-next": "^12.3.3", + "@next/eslint-plugin-next": "^12.3.4", "@types/crypto-js": "^4.1.1", "@types/git-clone": "^0.2.0", - "@types/node": "^16.18.3", - "@types/react": "^17.0.52", - "@types/react-dom": "^17.0.18", - "@types/uuid": "^8.3.4", - "@typescript-eslint/eslint-plugin": "^5.42.1", - "@typescript-eslint/parser": "^5.42.1", - "cypress": "^11.0.1", + "@types/node": "^16.18.16", + "@types/react": "^18.0.28", + "@types/react-dom": "^18.0.11", + "@typescript-eslint/eslint-plugin": "^5.55.0", + "@typescript-eslint/parser": "^5.55.0", + "cypress": "^11.2.0", "cypress-file-upload": "^5.0.8", - "eslint": "^8.27.0", + "eslint": "^8.36.0", "eslint-config-airbnb": "^19.0.4", "eslint-config-airbnb-typescript": "^17.0.0", - "eslint-config-prettier": "^8.5.0", - "eslint-plugin-import": "^2.26.0", - "eslint-plugin-jsx-a11y": "^6.6.1", - "eslint-plugin-react": "^7.31.10", + "eslint-config-prettier": "^8.7.0", + "eslint-plugin-import": "^2.27.5", + "eslint-plugin-jsx-a11y": "^6.7.1", + "eslint-plugin-react": "^7.32.2", "eslint-plugin-react-hooks": "^4.6.0", - "prettier": "^2.7.1" + "prettier": "^2.8.4" } } diff --git a/pages/_app.tsx b/pages/_app.tsx index 8e22c751..b9636758 100644 --- a/pages/_app.tsx +++ b/pages/_app.tsx @@ -12,7 +12,7 @@ import 'theme/index.css'; import Auth from 'lib/auth'; import User from 'lib/user'; -import { IPCConfig, IPCContact, IPCFile, IPCFolder } from 'types/types'; +import { IPCConfig, IPCContact, IPCFile, IPCFolder, IPCProgram } from 'types/types'; import AuthContext from 'contexts/auth'; import ConfigContext from 'contexts/config'; @@ -30,7 +30,9 @@ const App = ({ const [config, setConfig] = useState(undefined); const [error, setError] = useState(undefined); const [files, setFiles] = useState([]); + const [sharedFiles, setSharedFiles] = useState([]); const [folders, setFolders] = useState([]); + const [programs, setPrograms] = useState([]); const [contacts, setContacts] = useState([]); const [path, setPath] = useState('/'); const toast = useToast(); @@ -83,8 +85,12 @@ const App = ({ value={{ files, setFiles, + sharedFiles, + setSharedFiles, folders, setFolders, + programs, + setPrograms, contacts, setContacts, path, diff --git a/pages/api/computing/github/repositories.ts b/pages/api/computing/github/repositories.ts index 3f85653d..7488e564 100644 --- a/pages/api/computing/github/repositories.ts +++ b/pages/api/computing/github/repositories.ts @@ -13,11 +13,23 @@ export default async (req: NextApiRequest, res: NextApiResponse): Promise }); if (userInfo.status !== 200) return res.status(userInfo.status).json({ error: userInfo.statusText }); - const userRepos = await axios.get('https://api.github.com/user/repos', { + const userRepos = await axios.get(`https://api.github.com/user/repos?page=${req.query.page || 0}`, { headers: { Authorization: `token ${session.accessToken}`, }, }); if (userRepos.status !== 200) return res.status(userRepos.status).json({ error: userRepos.statusText }); - return res.status(200).json(userRepos.data); + + const userMoreRepos = async (): Promise => { + if (userRepos.data.length < 30) return 0; + return ( + await axios.get(`https://api.github.com/user/repos?page=${Number(req.query.page) + 1 || 1}`, { + headers: { + Authorization: `token ${session.accessToken}`, + }, + }) + ).data.length; + }; + + return res.status(200).json({ repositories: userRepos.data, hasMore: (await userMoreRepos()) > 0 }); }; diff --git a/pages/connection.tsx b/pages/connection.tsx index 60f0f493..c13dcc3f 100644 --- a/pages/connection.tsx +++ b/pages/connection.tsx @@ -1,26 +1,35 @@ -import { Button } from '@chakra-ui/react'; -import Link from 'next/link'; +import { Link, Stack, useBreakpointValue } from '@chakra-ui/react'; +import { BsRocketTakeoffFill } from 'react-icons/bs'; import AuthPage from 'components/AuthPage'; -import OutlineButton from 'components/OutlineButton'; +import Button from 'components/Button'; -const Connection = (): JSX.Element => ( - - - - -
- -
+ + - - } - /> -); + +
+ ); +}; export default Connection; diff --git a/pages/dashboard.tsx b/pages/dashboard.tsx deleted file mode 100644 index 1bbf81e2..00000000 --- a/pages/dashboard.tsx +++ /dev/null @@ -1,271 +0,0 @@ -import axios from 'axios'; -import { signIn, signOut, useSession } from 'next-auth/react'; -import { useRouter } from 'next/router'; -import { ChangeEvent, useEffect, useState } from 'react'; - -import { Box, Button, HStack, Input, Select, useColorMode, useDisclosure, useToast, VStack } from '@chakra-ui/react'; - -import { useUserContext } from 'contexts/user'; - -import type { GitHubRepository, IPCFile, IPCProgram } from 'types/types'; - -import Modal from 'components/Modal'; - -import { extractFilename } from 'utils/fileManipulation'; - -import CustomProgram from 'components/computing/CustomProgram'; -import DisplayCards from 'components/DisplayCards'; -import { ResponsiveBar } from 'components/ResponsiveBar'; -import { useConfigContext } from 'contexts/config'; -import { useDriveContext } from 'contexts/drive'; - -const Dashboard = (): JSX.Element => { - const toast = useToast({ duration: 2000, isClosable: true }); - const router = useRouter(); - const { user } = useUserContext(); - const { config, setConfig } = useConfigContext(); - const { colorMode, toggleColorMode } = useColorMode(); - const { isOpen: isOpenProgram, onOpen: onOpenProgram, onClose: onCloseProgram } = useDisclosure(); - const { isOpen: isOpenGithub, onOpen: onOpenGithub, onClose: onCloseGithub } = useDisclosure(); - const { setFiles, setFolders, setContacts } = useDriveContext(); - const [programs, setPrograms] = useState([]); - const [sharedFiles, setSharedFiles] = useState([]); - const [selectedTab, setSelectedTab] = useState(0); - const [isDeployLoading, setIsDeployLoading] = useState(false); - const [isGithubLoading] = useState(false); - const [fileEvent, setFileEvent] = useState | undefined>(undefined); - const [selectedProgram, setSelectedProgram] = useState({ - name: '', - hash: '', - createdAt: 0, - entrypoint: '', - }); - const [repositories, setRepositories] = useState([]); - const [selectedRepository, setSelectedRepository] = useState(''); - const [customName, setCustomName] = useState(''); - const [customEntrypoint, setCustomEntrypoint] = useState(''); - const { data: session } = useSession(); - - useEffect(() => { - (async () => { - if (!user) { - router.push('/'); - } else { - await loadContact(); - await loadUserContents(); - if (session) await getRepositories(); - } - })(); - }, []); - - const loadContact = async () => { - const load = await user.contact.load(); - - toast({ title: load.message, status: load.success ? 'success' : 'error' }); - setContacts(user.contact.contacts); - }; - - const loadUserContents = async () => { - const loadShared = await user.drive.loadShared(user.contact.contacts); - toast({ title: loadShared.message, status: loadShared.success ? 'success' : 'error' }); - setFiles(user.drive.files); - setFolders(user.drive.folders); - setSharedFiles(user.drive.sharedFiles); - - const loadedPrograms = await user.computing.load(); - toast({ title: loadedPrograms.message, status: loadedPrograms.success ? 'success' : 'error' }); - setPrograms(user.computing.programs); - - const loadedConfig = await user.loadConfig(); - setConfig(user.config); - if (user.config?.theme === 'white' && colorMode !== 'light') toggleColorMode(); - if (user.config?.theme === 'gray.800' && colorMode !== 'dark') toggleColorMode(); - toast({ title: loadedConfig.message, status: loadedConfig.success ? 'success' : 'error' }); - }; - - const uploadProgram = async (oldProgram: IPCProgram | undefined) => { - if (!fileEvent || !fileEvent.target.files) return; - const filename = extractFilename(fileEvent.target.value); - if (!filename) return; - - setIsDeployLoading(true); - try { - const upload = await user.computing.uploadProgram( - { - name: customName || filename, - hash: '', - createdAt: Date.now(), - entrypoint: customEntrypoint || user.config?.defaultEntrypoint || 'main:app', - }, - fileEvent.target.files[0], - !!oldProgram, - oldProgram, - ); - toast({ title: upload.message, status: upload.success ? 'success' : 'error' }); - setPrograms(user.computing.programs); - onCloseProgram(); - } catch (error) { - console.error(error); - toast({ title: 'Unable to upload file', status: 'error' }); - } - setFileEvent(undefined); - setIsDeployLoading(false); - }; - - const getRepositories = async () => { - try { - const result = await axios.get('/api/computing/github/repositories'); - if (result.status !== 200) throw new Error("Unable to load repositories from Github's API"); - setRepositories(result.data); - } catch (error) { - console.error(error); - } - }; - - const cloneToBackend = async (repository: string) => { - try { - setIsDeployLoading(true); - const result = await axios.post('/api/program/create', { - repository: `${repository}.git`, - entrypoint: customEntrypoint || user.config?.defaultEntrypoint || 'main:app', - }); - if (result.status !== 200) throw new Error('Unable to clone repository from Github'); - const newProgram: IPCProgram = { - name: customName || result.data.name, - hash: result.data.item_hash, - createdAt: Date.now(), - entrypoint: result.data.entrypoint, - }; - user.computing.programs.push(newProgram); - await user.computing.publishAggregate(); - setPrograms(user.computing.programs); - toast({ title: 'Upload succeeded', status: 'success' }); - onCloseGithub(); - } catch (err) { - toast({ title: 'Upload failed', status: 'error' }); - console.error(err); - } - setIsDeployLoading(false); - setCustomEntrypoint(''); - setCustomName(''); - }; - - return ( - - - - - - - - - - uploadProgram(selectedProgram)} - isLoading={isDeployLoading} - id="ipc-dashboard-deploy-program-modal-button" - > - Deploy program - - } - > - <> - - ) => setFileEvent(e)} - id="ipc-dashboard-deploy-program" - /> - - - { - cloneToBackend(selectedRepository); - }} - isLoading={isDeployLoading} - id="ipc-dashboard-deploy-from-github-modal-button" - > - Deploy program - - } - > - <> - {!session && ( - - )} - {session && ( - - - - - - )} - - - - ); -}; - -export default Dashboard; diff --git a/pages/drive/account.tsx b/pages/drive/account.tsx new file mode 100644 index 00000000..27ca7aac --- /dev/null +++ b/pages/drive/account.tsx @@ -0,0 +1,18 @@ +import { VStack } from '@chakra-ui/react'; + +import AccountCard from 'components/account/AccountCard'; +import ConfigCard from 'components/account/ConfigCard'; +import LabelBadge from 'components/LabelBadge'; +import Navigation from 'components/navigation/Navigation'; + +const Account = (): JSX.Element => ( + + + + + + + +); + +export default Account; diff --git a/pages/drive/contacts.tsx b/pages/drive/contacts.tsx new file mode 100644 index 00000000..fca5efe4 --- /dev/null +++ b/pages/drive/contacts.tsx @@ -0,0 +1,26 @@ +import { HStack, VStack } from '@chakra-ui/react'; + +import DriveCards from 'components/cards/DriveCards'; +import AddContact from 'components/contact/AddContact'; +import LabelBadge from 'components/LabelBadge'; +import Navigation from 'components/navigation/Navigation'; + +import { useDriveContext } from 'contexts/drive'; + +const Contacts = (): JSX.Element => { + const { contacts } = useDriveContext(); + + return ( + + + + + + + + + + ); +}; + +export default Contacts; diff --git a/pages/drive/index.tsx b/pages/drive/index.tsx new file mode 100644 index 00000000..e2a548c2 --- /dev/null +++ b/pages/drive/index.tsx @@ -0,0 +1,67 @@ +import { useColorMode, useToast, VStack } from '@chakra-ui/react'; +import { useEffect } from 'react'; + +import { useConfigContext } from 'contexts/config'; +import { useDriveContext } from 'contexts/drive'; +import { useUserContext } from 'contexts/user'; + +import DriveCards from 'components/cards/DriveCards'; +import LabelBadge from 'components/LabelBadge'; +import Navigation from 'components/navigation/Navigation'; + +const Dashboard = (): JSX.Element => { + const toast = useToast({ duration: 2000, isClosable: true }); + const { user } = useUserContext(); + const { setConfig } = useConfigContext(); + const { colorMode, toggleColorMode } = useColorMode(); + + const { path, folders, files, setFiles, setFolders, setContacts, setPrograms, setSharedFiles } = useDriveContext(); + + useEffect(() => { + (async () => { + if (user) { + await loadContact(); + await loadUserContents(); + } + })(); + }, []); + + const loadContact = async () => { + const load = await user.contact.load(); + + toast({ title: load.message, status: load.success ? 'success' : 'error' }); + setContacts(user.contact.contacts); + }; + + const loadUserContents = async () => { + const loadShared = await user.drive.loadShared(user.contact.contacts); + toast({ title: loadShared.message, status: loadShared.success ? 'success' : 'error' }); + setFiles(user.drive.files); + setFolders(user.drive.folders); + setSharedFiles(user.drive.sharedFiles); + + const loadedPrograms = await user.computing.load(); + toast({ title: loadedPrograms.message, status: loadedPrograms.success ? 'success' : 'error' }); + setPrograms(user.computing.programs); + + const loadedConfig = await user.loadConfig(); + setConfig(user.config); + if (user.config?.theme.value === 'white' && colorMode !== 'light') toggleColorMode(); + if (user.config?.theme.value === 'gray.800' && colorMode !== 'dark') toggleColorMode(); + toast({ title: loadedConfig.message, status: loadedConfig.success ? 'success' : 'error' }); + }; + + return ( + + + + elem.path === path && !elem.deletedAt)} + folders={folders.filter((elem) => elem.path === path)} + /> + + + ); +}; + +export default Dashboard; diff --git a/pages/drive/programs.tsx b/pages/drive/programs.tsx new file mode 100644 index 00000000..c85ac62e --- /dev/null +++ b/pages/drive/programs.tsx @@ -0,0 +1,22 @@ +import { VStack } from '@chakra-ui/react'; + +import DriveCards from 'components/cards/DriveCards'; +import LabelBadge from 'components/LabelBadge'; +import Navigation from 'components/navigation/Navigation'; + +import { useDriveContext } from 'contexts/drive'; + +const Programs = (): JSX.Element => { + const { programs } = useDriveContext(); + + return ( + + + + + + + ); +}; + +export default Programs; diff --git a/pages/drive/shared.tsx b/pages/drive/shared.tsx new file mode 100644 index 00000000..42fee177 --- /dev/null +++ b/pages/drive/shared.tsx @@ -0,0 +1,22 @@ +import { VStack } from '@chakra-ui/react'; + +import DriveCards from 'components/cards/DriveCards'; +import LabelBadge from 'components/LabelBadge'; +import Navigation from 'components/navigation/Navigation'; + +import { useDriveContext } from 'contexts/drive'; + +const Shared = (): JSX.Element => { + const { sharedFiles } = useDriveContext(); + + return ( + + + + !elem.deletedAt)} /> + + + ); +}; + +export default Shared; diff --git a/pages/drive/trash.tsx b/pages/drive/trash.tsx new file mode 100644 index 00000000..1dbc0e3b --- /dev/null +++ b/pages/drive/trash.tsx @@ -0,0 +1,29 @@ +import { HStack, VStack } from '@chakra-ui/react'; + +import DriveCards from 'components/cards/DriveCards'; +import DeleteBin from 'components/file/DeleteBin'; +import LabelBadge from 'components/LabelBadge'; +import Navigation from 'components/navigation/Navigation'; + +import { useDriveContext } from 'contexts/drive'; + +const Trash = (): JSX.Element => { + const { path, files, folders, sharedFiles } = useDriveContext(); + + const deletedFiles = files.filter((elem) => elem.path === path && elem.deletedAt !== null); + const deletedFolders = folders.filter((elem) => elem.path === path); + + return ( + + + + + + + + + + ); +}; + +export default Trash; diff --git a/pages/index.tsx b/pages/index.tsx index 819a8f0d..daf8bd32 100644 --- a/pages/index.tsx +++ b/pages/index.tsx @@ -1,11 +1,11 @@ import { VStack } from '@chakra-ui/react'; -import NavBar from 'components/landingPage/NavBar'; +import FeaturesSection from 'components/landingPage/FeaturesSection'; +import Footer from 'components/landingPage/Footer'; import HeadingSection from 'components/landingPage/HeadingSection'; +import NavBar from 'components/landingPage/NavBar'; import PartnersSection from 'components/landingPage/PartnersSection'; import ServicesSection from 'components/landingPage/ServicesSection'; -import FeaturesSection from 'components/landingPage/FeaturesSection'; -import Footer from 'components/landingPage/Footer'; const Home = (): JSX.Element => ( diff --git a/pages/login.tsx b/pages/login.tsx index d967b2fc..e7123e5e 100644 --- a/pages/login.tsx +++ b/pages/login.tsx @@ -1,16 +1,18 @@ +import { Text, Textarea, useColorModeValue, useToast, VStack } from '@chakra-ui/react'; import Link from 'next/link'; import { useRouter } from 'next/router'; import { useState } from 'react'; -import { Button, FormControl, Text, Textarea, useToast, VStack } from '@chakra-ui/react'; - import { useAuthContext } from 'contexts/auth'; +import { useConfigContext } from 'contexts/config'; import { useUserContext } from 'contexts/user'; import AuthPage from 'components/AuthPage'; -import OutlineButton from 'components/OutlineButton'; +import Button from 'components/Button'; -import { useConfigContext } from 'contexts/config'; +import { ResponseType } from 'types/types'; + +import { textColorMode } from 'config/colorMode'; import colors from 'theme/foundations/colors'; const Login = (): JSX.Element => { @@ -23,27 +25,37 @@ const Login = (): JSX.Element => { const [isLoadingCredentials, setIsLoadingCredentials] = useState(false); const toast = useToast({ duration: 2000, isClosable: true }); + const textColor = useColorModeValue(textColorMode.light, textColorMode.dark); - const loginWithCredentials = async (): Promise => { + const loginWithCredentials = async (): Promise => { setIsLoadingCredentials(true); const login = await auth.loginWithCredentials(mnemonics, config); setIsLoadingCredentials(false); - if (login.user) { - toast({ title: login.message, status: 'success' }); - setUser(login.user); - router.push('/dashboard'); - await login.user.drive.autoDelete() - } else { - toast({ title: login.message, status: 'error' }); - } + if (!login.user) return { success: false, message: login.message }; + setUser(login.user); + await router.push('/drive'); + await login.user.drive.autoDelete(); + return { success: true, message: login.message }; + }; + + const loginWithAProvider = async (): Promise => { + setIsLoadingCredentials(true); + const login = await auth.loginWithProvider(config); + setIsLoadingCredentials(false); + + if (!login.user) return { success: false, message: login.message }; + setUser(login.user); + await router.push('/drive'); + await login.user.drive.autoDelete(); + return { success: true, message: login.message }; }; return ( - - + + + +