diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 0000000..1f0d754
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1,7 @@
+build
+.github
+node_modules
+.eslintignore
+.eslintrc.json
+.prettierignore
+.prettierrc
diff --git a/.env.example b/.env.example
new file mode 100644
index 0000000..909276b
--- /dev/null
+++ b/.env.example
@@ -0,0 +1,3 @@
+CLIENT_ID =
+BACKEND_URL =
+AVATAR_API =
\ No newline at end of file
diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml
new file mode 100644
index 0000000..5666e66
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug_report.yml
@@ -0,0 +1,96 @@
+
+name: Bug Report
+title: "[Bug] Bug title "
+description: Create a report to help us identify any unintended flaws, errors, or faults.
+body:
+ - type: checkboxes
+ attributes:
+ label: Before Creating the Bug Report
+ options:
+ - label: >
+ I found a bug, not just asking a question, which should be created in [GitHub Discussions](https://github.com/mdgspace/activity-leaderboard-backend/discussions).
+ required: true
+ - label: >
+ I have searched the [GitHub Issues](https://github.com/mdgspace/activity-leaderboard-backend/issues) and [GitHub Discussions](https://github.com/mdgspace/activity-leaderboard-backend/discussions) of this repository and believe that this is not a duplicate.
+ required: true
+ - label: >
+ I have confirmed that this bug belongs to the current repository, not other repositories of RocketMQ.
+ required: true
+
+ - type: textarea
+ attributes:
+ label: Runtime platform environment
+ description: Describe the runtime platform environment.
+ placeholder: >
+ OS: (e.g., "Ubuntu 20.04")
+ OS: (e.g., "Windows Server 2019")
+ validations:
+ required: true
+
+ - type: textarea
+ attributes:
+ label: Backend version
+ description: Describe the Backend version.
+ placeholder: >
+ branch: (e.g main)
+ version: (e.g. 1.0.0)
+ Git commit id: (e.g. c88b5cfa72e204962929eea105687647146112c6)
+ validations:
+ required: true
+
+ - type: textarea
+ attributes:
+ label: JDK Version
+ description: Run or Compiler version.
+ placeholder: >
+ Compiler: (e.g., "Oracle JDK 11.0.17")
+ OS: (e.g., "Ubuntu 20.04")
+ Runtime (if different from JDK above): (e.g., "Oracle JRE 8u251")
+ OS (if different from OS compiled on): (e.g., "Windows Server 2019")
+ validations:
+ required: false
+
+ - type: textarea
+ attributes:
+ label: Describe the Bug
+ description: Describe what happened.
+ placeholder: >
+ A clear and concise description of what the bug is.
+ validations:
+ required: true
+
+ - type: textarea
+ attributes:
+ label: Steps to Reproduce
+ description: Describe the steps to reproduce the bug here.
+ placeholder: >
+ If possible, provide a recipe for reproducing the error.
+ validations:
+ required: true
+
+ - type: textarea
+ attributes:
+ label: What Did You Expect to See?
+ description: You expect to see result.
+ placeholder: >
+ A clear and concise description of what you expected to see.
+ validations:
+ required: true
+
+ - type: textarea
+ attributes:
+ label: What Did You See Instead?
+ description: You instead to see result.
+ placeholder: >
+ A clear and concise description of what you saw instead.
+ validations:
+ required: true
+
+ - type: textarea
+ attributes:
+ label: Additional Context
+ description: Additional context.
+ placeholder: >
+ Add any other context about the problem here.
+ validations:
+ required: false
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
new file mode 100644
index 0000000..8a095cf
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/config.yml
@@ -0,0 +1,7 @@
+
+
+blank_issues_enabled: false
+contact_links:
+ - name: Ask Question
+ url: https://github.com/mdgspace/activity-leaderboard-backend/discussions
+ about: Please go to GitHub Disccusions to ask questions
diff --git a/.github/ISSUE_TEMPLATE/doc.yml b/.github/ISSUE_TEMPLATE/doc.yml
new file mode 100644
index 0000000..540229f
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/doc.yml
@@ -0,0 +1,39 @@
+
+name: Documentation Related
+title: "[Doc] Documentation Related "
+description: I find some issues related to the documentation.
+labels: [ "module/doc" ]
+body:
+ - type: checkboxes
+ attributes:
+ label: Search before creation
+ description: >
+ Please make sure to search in the [issues](https://github.com/mdgspace/activity-leaderboard-backend/issues)
+ first to see whether the same issue was reported already.
+ options:
+ - label: >
+ I had searched in the [issues](https://github.com/mdgspace/activity-leaderboard-backend/issues) and found
+ no similar issues.
+ required: true
+
+ - type: textarea
+ attributes:
+ label: Documentation Related
+ description: Describe the suggestion about document.
+ placeholder: >
+ e.g There is a typo
+ validations:
+ required: true
+
+ - type: checkboxes
+ attributes:
+ label: Are you willing to submit PR?
+ description: >
+ This is absolutely not required, but we are happy to guide you in the contribution process
+ especially if you already have a good understanding of how to implement the fix.
+ options:
+ - label: Yes I am willing to submit a PR!
+
+ - type: markdown
+ attributes:
+ value: "Thanks for completing our form!"
diff --git a/.github/ISSUE_TEMPLATE/enhancement_request.yml b/.github/ISSUE_TEMPLATE/enhancement_request.yml
new file mode 100644
index 0000000..ab04414
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/enhancement_request.yml
@@ -0,0 +1,59 @@
+
+
+name: Enhancement Request
+title: "[Enhancement] Enhancement title"
+description: Suggest an enhancement for this project
+labels: [ "type/enhancement" ]
+body:
+ - type: checkboxes
+ attributes:
+ label: Before Creating the Enhancement Request
+ description: >
+ Most of issues should be classified as bug or feature request. An issue should be considered as an enhancement when it proposes improvements to
+ existing functionality or user experience, without necessarily introducing new features or fixing existing bugs.
+ options:
+ - label: >
+ I have confirmed that this should be classified as an enhancement rather than a bug/feature.
+ required: true
+
+ - type: textarea
+ attributes:
+ label: Summary
+ placeholder: >
+ A clear and concise description of the enhancement you would like to see in the project.
+ validations:
+ required: true
+
+ - type: textarea
+ attributes:
+ label: Motivation
+ placeholder: >
+ Explain why you believe this enhancement is necessary, and how it benefits the project and community.
+ Include any specific use cases that you have in mind.
+ validations:
+ required: true
+
+ - type: textarea
+ attributes:
+ label: Describe the Solution You'd Like
+ placeholder: >
+ Describe the enhancement you propose, detailing the change and implementation steps involved.
+ If you have multiple solutions, please list them separately.
+ validations:
+ required: true
+
+ - type: textarea
+ attributes:
+ label: Describe Alternatives You've Considered
+ placeholder: >
+ List any alternative enhancements or implementations you have considered, and explain why they may not be as effective or appropriate.
+ validations:
+ required: true
+
+ - type: textarea
+ attributes:
+ label: Additional Context
+ placeholder: >
+ Add any relevant context, screenshots, prototypes, or other supplementary information to help illustrate the enhancement.
+ validations:
+ required: false
diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml
new file mode 100644
index 0000000..435718e
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/feature_request.yml
@@ -0,0 +1,41 @@
+
+name: Feature Request
+title: "[Feature] New feature title"
+description: Suggest an idea for this project.
+labels: [ "type/new feature" ]
+body:
+ - type: textarea
+ attributes:
+ label: Is Your Feature Request Related to a Problem?
+ description: Please Describe It.
+ placeholder: >
+ A clear and concise description of what the problem is.
+ validations:
+ required: true
+
+ - type: textarea
+ attributes:
+ label: Describe the Solution You'd Like
+ description: Describe how you solved it.
+ placeholder: >
+ A clear and concise description of what you want to happen.
+ validations:
+ required: true
+
+ - type: textarea
+ attributes:
+ label: Describe Alternatives You've Considered
+ description: Describe your solution
+ placeholder: >
+ A clear and concise description of any alternative solutions or features you've considered.
+ validations:
+ required: true
+
+ - type: textarea
+ attributes:
+ label: Additional Context
+ description: Additional context.
+ placeholder: >
+ Add any other context about the problem here.
+ validations:
+ required: false
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
new file mode 100644
index 0000000..96bffa5
--- /dev/null
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -0,0 +1,15 @@
+
+
+### Which Issue(s) This PR Fixes
+
+
+
+Fixes #issue_id
+
+### Brief Description
+
+
+
+### How Did You Test This Change?
+
+
diff --git a/DEPLOYMENT.md b/DEPLOYMENT.md
new file mode 100644
index 0000000..5e08480
--- /dev/null
+++ b/DEPLOYMENT.md
@@ -0,0 +1,23 @@
+# Deployment Guide
+
+## Prerequisites
+- [Git](https://git-scm.com/downloads)
+- [Docker](https://docs.docker.com/engine/install/)
+- [MultiAvatar api secret Key](https://api.multiavatar.com/)
+- [Github OAuth App](https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/creating-an-oauth-app)
+
+## Steps
+
+### Clone the repo
+```
+ git clone https://github.com/mdgspace/activity-leaderboard.git
+
+ cd activity-leaderboard
+```
+
+### Create .env using .env.example
+
+### Run docker-compose
+```
+docker compose up -d
+```
diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md
new file mode 100644
index 0000000..7fd9222
--- /dev/null
+++ b/DEVELOPMENT.md
@@ -0,0 +1,31 @@
+# Deployment Guide
+
+## Prerequisites
+- [Git](https://git-scm.com/downloads)
+- [Docker](https://docs.docker.com/engine/install/)
+- [nodejs](https://nodejs.org/en/download)
+- [MultiAvatar api secret Key](https://api.multiavatar.com/)
+- [Github OAuth App](https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/creating-an-oauth-app)
+
+## Steps
+
+### Clone the repo
+```
+ git clone https://github.com/mdgspace/activity-leaderboard.git
+
+ cd activity-leaderboard
+```
+
+### Create .env using .env.example
+
+### Run
+1. `npm ci`
+2. `npm run prepare`
+3. `npm run lint`
+4. `npm run prettier`
+
+### Run appplication
+
+```
+npm start
+```
\ No newline at end of file
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..b871830
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,20 @@
+FROM node:latest as build
+
+WORKDIR /app
+
+COPY package.json package-lock.json ./
+
+RUN npm install
+
+COPY . ./
+
+RUN npm run build
+
+
+FROM nginx:alpine
+
+COPY --from=build /app/build /usr/share/nginx/html
+
+EXPOSE 80
+
+CMD ["nginx", "-g", "daemon off;"]
diff --git a/Dockerfile.dev b/Dockerfile.dev
new file mode 100644
index 0000000..bd35dd0
--- /dev/null
+++ b/Dockerfile.dev
@@ -0,0 +1,24 @@
+# Start from a Node base image
+FROM node:latest
+# Set the working directory
+WORKDIR /app
+# Set environment variables
+ARG CLIENT_ID
+ENV CLIENT_ID=${CLIENT_ID}
+ARG BACKEND_URL
+ENV BACKEND_URL=${BACKEND_URL}
+ARG AVATAR_API
+ENV AVATAR_API=${AVATAR_API}
+# Copy package.json and package-lock.json
+COPY package*.json ./
+# Install dependencies
+RUN npm install
+# Copy the rest of the code
+COPY . .
+# Build the app
+RUN npm run build
+# Expose the port the app runs on
+EXPOSE 3000
+# Start the application
+CMD ["npm", "start"]
+
diff --git a/README.md b/README.md
index 9d368cc..788aa04 100644
--- a/README.md
+++ b/README.md
@@ -1,8 +1,21 @@
-# Activity Leaderboard
+# Activity-leaderboard Frontend
-## Setup
+### Activity Leader Board is a open source project which can be used to monitor progress in an Organization .
-1. `npm ci`
-2. `npm run prepare`
-3. `npm run lint`
-4. `npm run prettier`
+
+
+## Tech Stack
+
+**Backend:** SpringBoot
+
+**Frontend:** React
+
+**Cloud:** AWS
+
+**Database**: Postgres
+
+**Caching**: Redis
+
+
+### Activity-leaderboard Backend
+[Backend](https://github.com/mdgspace/activity-leaderboard.git)
\ No newline at end of file
diff --git a/docker-compose.yaml b/docker-compose.yaml
index 6df6d6e..c6b91e0 100644
--- a/docker-compose.yaml
+++ b/docker-compose.yaml
@@ -1,11 +1,15 @@
-version: '3.3'
+version: '3.8'
+
services:
- api:
- build:
- context: ./
- dockerfile: local.Dockerfile
- volumes:
- - ./:/app
- - ./.m2:/root/.m2
- working_dir: /app
- command: sh run.sh
\ No newline at end of file
+ frontend:
+ build:
+ context: .
+ dockerfile: Dockerfile
+ env_file:
+ - .env
+ ports:
+ - "8081:3000"
+ volumes:
+ - .:/app
+ stdin_open: true
+ tty: true
diff --git a/package-lock.json b/package-lock.json
index 28f414a..2c3c5ab 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -8,7 +8,7 @@
"name": "activity-leaderboard",
"version": "0.1.0",
"dependencies": {
- "@reduxjs/toolkit": "^1.9.6",
+ "@reduxjs/toolkit": "^1.9.7",
"@testing-library/jest-dom": "^6.1.3",
"@testing-library/react": "^14.0.0",
"@testing-library/user-event": "^14.5.1",
@@ -18,6 +18,7 @@
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-hot-toast": "^2.4.1",
+ "react-icons": "^5.0.1",
"react-query": "^3.39.3",
"react-redux": "^8.1.3",
"react-router-dom": "^6.16.0",
@@ -3728,9 +3729,9 @@
}
},
"node_modules/@reduxjs/toolkit": {
- "version": "1.9.6",
- "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-1.9.6.tgz",
- "integrity": "sha512-Gc4ikl90ORF4viIdAkY06JNUnODjKfGxZRwATM30EdHq8hLSVoSrwXne5dd739yenP5bJxAX7tLuOWK5RPGtrw==",
+ "version": "1.9.7",
+ "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-1.9.7.tgz",
+ "integrity": "sha512-t7v8ZPxhhKgOKtU+uyJT13lu4vL7az5aFi4IdoDs/eS548edn2M8Ik9h8fxgvMjGoAUVFSt6ZC1P5cWmQ014QQ==",
"dependencies": {
"immer": "^9.0.21",
"redux": "^4.2.1",
@@ -16271,6 +16272,14 @@
"react-dom": ">=16"
}
},
+ "node_modules/react-icons": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.0.1.tgz",
+ "integrity": "sha512-WqLZJ4bLzlhmsvme6iFdgO8gfZP17rfjYEJ2m9RsZjZ+cc4k1hTzknEz63YS1MeT50kVzoa1Nz36f4BEx+Wigw==",
+ "peerDependencies": {
+ "react": "*"
+ }
+ },
"node_modules/react-is": {
"version": "17.0.2",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
diff --git a/package.json b/package.json
index 134284a..82e6d0c 100644
--- a/package.json
+++ b/package.json
@@ -3,7 +3,7 @@
"version": "0.1.0",
"private": true,
"dependencies": {
- "@reduxjs/toolkit": "^1.9.6",
+ "@reduxjs/toolkit": "^1.9.7",
"@testing-library/jest-dom": "^6.1.3",
"@testing-library/react": "^14.0.0",
"@testing-library/user-event": "^14.5.1",
@@ -13,6 +13,7 @@
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-hot-toast": "^2.4.1",
+ "react-icons": "^5.0.1",
"react-query": "^3.39.3",
"react-redux": "^8.1.3",
"react-router-dom": "^6.16.0",
diff --git a/public/index.html b/public/index.html
index 2402be8..0ecb1c9 100644
--- a/public/index.html
+++ b/public/index.html
@@ -28,6 +28,8 @@
href="https://fonts.googleapis.com/css?family=Poppins"
rel="stylesheet"
/>
+
+
Activity Leaderboard
diff --git a/src/app/api/.gitkeep b/src/app/api/.gitkeep
deleted file mode 100644
index e69de29..0000000
diff --git a/src/app/api/file.ts b/src/app/api/file.ts
index b187ec4..607ec01 100644
--- a/src/app/api/file.ts
+++ b/src/app/api/file.ts
@@ -1,23 +1,28 @@
import axios, { AxiosResponse } from 'axios';
import { BACKEND_URL } from 'envConstants';
+export interface FileUpload {
+ message: string;
+ isSuccessful: boolean;
+ statusCode: number;
+}
-export interface FileUpload{
-
- message: string,
- isSuccessful: boolean,
- statusCode: number,
-
+export interface IconNameRes {
+ message: string;
}
+
+
+
+
export const uploadIcon = async (
authorizationToken: string,
orgName: string,
file: File
-):Promise> => {
+): Promise> => {
const url = BACKEND_URL + '/api/protected/file/upload/' + orgName;
const formData = new FormData();
formData.append('file', file);
@@ -30,16 +35,28 @@ export const uploadIcon = async (
return respnse;
};
+
+
+
+
export const getIcon = async (authorizationToken: string, orgName: string) => {
const url = BACKEND_URL + '/api/protected/file/getIcon/' + orgName;
- const response = await axios.get(url, {
- headers: {
- Authorization: `Bearer ${authorizationToken}`,
- },
- });
+ const response = await axios.get(
+ url,
+
+ {
+ responseType: 'blob',
+ headers: {
+ Authorization: `Bearer ${authorizationToken}`,
+ Accept: '*/*',
+ },
+ }
+ );
return response;
};
+
+
export const deleteFile = async (
authorizationToken: string,
fileName: string
@@ -53,3 +70,20 @@ export const deleteFile = async (
});
return respnse;
};
+
+
+
+
+export const getIconName = async (
+ authorizationToken: string,
+ orgName: string
+): Promise> => {
+ const url = BACKEND_URL + '/api/protected/file/getIconName/' + orgName;
+ const response = await axios.get(url, {
+ headers: {
+ Accept: 'application/json',
+ Authorization: `Bearer ${authorizationToken}`,
+ },
+ });
+ return response;
+};
diff --git a/src/app/api/githubData.ts b/src/app/api/githubData.ts
index 6916685..665dfae 100644
--- a/src/app/api/githubData.ts
+++ b/src/app/api/githubData.ts
@@ -1,42 +1,40 @@
import axios, { AxiosResponse } from 'axios';
import { BACKEND_URL } from 'envConstants';
-
-
export interface Contributors {
[contributorName: string]: {
- issues: number,
- pulls: number,
- commits:number
+ issues: number;
+ pulls: number;
+ commits: number;
};
}
export interface ProjectsGithubData {
[contributorName: string]: {
- issues: number,
- pulls: number,
- commits:number
+ issues: number;
+ pulls: number;
+ commits: number;
};
}
-
-export interface OrgRank{
- contributors: Contributors
+export interface OrgRank {
+ contributors: Contributors;
}
-
-// Contributors==project issues commits pull
-export interface OrgProjectGithubData{
- projects: ProjectsGithubData
+export interface ProjectStats {
+ contributors: Contributors;
}
+export interface OrgProjectGithubData {
+ projects: ProjectsGithubData;
+}
export const getOrgGithubData = async (
authorizationToken: string,
orgName: string,
monthly: boolean
-):Promise> => {
+): Promise> => {
const url =
BACKEND_URL + '/api/protected/github/' + orgName + '?monthly=' + monthly;
const respnse = await axios.get(url, {
@@ -73,7 +71,7 @@ export const getProjectGithubData = async (
orgName: string,
projectName: string,
monthly: boolean
-) => {
+): Promise> => {
const url =
BACKEND_URL +
'/api/protected/github/' +
@@ -82,7 +80,7 @@ export const getProjectGithubData = async (
projectName +
'?monthly=' +
monthly;
- const respnse = await axios.get(url, {
+ const respnse = await axios.get(url, {
headers: {
Accept: 'application/json',
Authorization: `Bearer ${authorizationToken}`,
diff --git a/src/app/api/login.ts b/src/app/api/login.ts
index 6f1f1c9..2fdfa66 100644
--- a/src/app/api/login.ts
+++ b/src/app/api/login.ts
@@ -2,15 +2,16 @@ import axios from 'axios';
import { BACKEND_URL } from 'envConstants';
import { AxiosResponse } from 'axios';
-
-export interface LoginData{
- token: string,
- username: string,
- type: string,
- id: number
+export interface LoginData {
+ token: string;
+ username: string;
+ type: string;
+ id: number;
}
-export const login = async (code: string):Promise> => {
+export const login = async (
+ code: string
+): Promise> => {
const url = BACKEND_URL + '/api/auth/login';
const respnse = await axios.post(
url,
diff --git a/src/app/api/organization.ts b/src/app/api/organization.ts
index 97ecc2c..24f758f 100644
--- a/src/app/api/organization.ts
+++ b/src/app/api/organization.ts
@@ -6,26 +6,34 @@ export interface organizationBody {
description: string;
}
-
-export interface AllOrgs{
+export interface AllOrgs {
organizations: {
- id: number,
- name: string,
- description: string|null
- }[]
+ id: number;
+ name: string;
+ description: string | null;
+ }[];
}
-export interface Projects{
- [ProjectName: string]:{
- archeive:boolean,
- bookmark: boolean
- }
+export interface Projects {
+ [ProjectName: string]: {
+ archeive: boolean;
+ bookmark: boolean;
+ };
}
-export interface OrgProjects{
- projects: Projects
+export interface OrgProjects {
+ projects: Projects;
}
-
+export interface Workspace {
+ id: number;
+ name: string;
+ description: string;
+}
+interface OrgMembers {
+ members: {
+ [username: string]: string;
+ };
+}
export const deleteOrg = async (
authorizationToken: string,
@@ -116,11 +124,11 @@ export const changeOrgMembersStatus = async (
orgName: string,
orgMemberStatus: { [key: string]: string }
) => {
- const url = BACKEND_URL + '/api/protected/org/removeMembers/';
+ const url = BACKEND_URL + '/api/protected/org/changeMembersStatus/' + orgName;
const respnse = await axios.put(
url,
{
- orgMemberStatus: orgMemberStatus,
+ orgMembersStatus: orgMemberStatus,
},
{
headers: {
@@ -179,10 +187,10 @@ export const setBookmarkStatus = async (
export const getOrgMembers = async (
authorizationToken: string,
orgName: string
-) => {
+): Promise> => {
const url = BACKEND_URL + '/api/protected/org/getMembers/' + orgName;
- const respnse = await axios.get(url, {
+ const respnse = await axios.get(url, {
headers: {
Accept: 'application/json',
Authorization: `Bearer ${authorizationToken}`,
@@ -194,7 +202,7 @@ export const getOrgMembers = async (
export const getOrgProjects = async (
authorizationToken: string,
orgName: string
-):Promise> => {
+): Promise> => {
const url = BACKEND_URL + '/api/protected/org/getProjects/' + orgName;
const respnse = await axios.get(url, {
headers: {
@@ -205,9 +213,12 @@ export const getOrgProjects = async (
return respnse;
};
-export const getOrg = async (authorizationToken: string, orgName: string) => {
- const url = BACKEND_URL + '/api/protected/org/getProjects/' + orgName;
- const respnse = await axios.get(url, {
+export const getOrg = async (
+ authorizationToken: string,
+ orgName: string
+): Promise> => {
+ const url = BACKEND_URL + '/api/protected/org/getOrg/' + orgName;
+ const respnse = await axios.get(url, {
headers: {
Accept: 'application/json',
Authorization: `Bearer ${authorizationToken}`,
@@ -216,7 +227,9 @@ export const getOrg = async (authorizationToken: string, orgName: string) => {
return respnse;
};
-export const getAllOrgs = async (authorizationToken: string): Promise> => {
+export const getAllOrgs = async (
+ authorizationToken: string
+): Promise> => {
const url = BACKEND_URL + '/api/protected/org/getAllOrg';
const respnse = await axios.get(url, {
headers: {
diff --git a/src/app/api/project.ts b/src/app/api/project.ts
index e604a3d..b0ae917 100644
--- a/src/app/api/project.ts
+++ b/src/app/api/project.ts
@@ -7,17 +7,18 @@ export interface projectBody {
link: string;
}
-export interface GetProject{
- id: number,
- name: string,
- description: string
+export interface GetProject {
+ id: number;
+ name: string;
+ description: string;
+ link: string;
}
-export interface Member{
- [key: string]:string
+export interface Member {
+ [key: string]: string;
}
-export interface ProjectMembers{
- members:Member
+export interface ProjectMembers {
+ members: Member;
}
export const addProject = async (
@@ -159,7 +160,7 @@ export const getProject = async (
authorizationToken: string,
projectName: string,
orgName: string
-):Promise> => {
+): Promise> => {
const url =
BACKEND_URL +
'/api/protected/project/getProject/' +
@@ -179,7 +180,7 @@ export const getMembers = async (
authorizationToken: string,
projectName: string,
orgName: string
-) : Promise>=> {
+): Promise> => {
const url =
BACKEND_URL +
'/api/protected/project/getMembers/' +
diff --git a/src/app/api/user.ts b/src/app/api/user.ts
index 5b6fbfd..677e4f0 100644
--- a/src/app/api/user.ts
+++ b/src/app/api/user.ts
@@ -1,38 +1,48 @@
-import axios,{AxiosResponse} from 'axios';
+import axios, { AxiosResponse } from 'axios';
import { BACKEND_URL } from 'envConstants';
-
-
-
-export interface UserData{
- message: string
+export interface UserData {
+ message: string;
}
+export interface UserOrgs {
+ userOrgs: UserOrgDetails;
+}
+export interface UserOrgDetails {
+ [key: string]: {
+ bookmark: string;
+ role: string;
+ archeive: string;
+ };
+}
-export interface AllUserData{
+export interface AllUserData {
users: {
- id: number,
- username: string
- }[]
+ id: number;
+ username: string;
+ }[];
}
-export const getUser= async (authorizationToken: string):Promise> => {
+export const getUser = async (
+ authorizationToken: string
+): Promise> => {
const url = BACKEND_URL + '/api/protected/user/getUser';
- const respnse = await axios.get(url, {
- headers: {
- Accept: 'application/json',
-
- Authorization: `Bearer ${authorizationToken}`,
- },
- });
-
- return respnse;
+ const respnse = await axios.get(url, {
+ headers: {
+ Accept: 'application/json',
+
+ Authorization: `Bearer ${authorizationToken}`,
+ },
+ });
+ return respnse;
};
-export const getAllUser = async (authorizationToken: string):Promise> => {
+export const getAllUser = async (
+ authorizationToken: string
+): Promise> => {
const url = BACKEND_URL + '/api/protected/user/all';
const respnse = await axios.get(url, {
headers: {
@@ -71,7 +81,7 @@ export const setOrgArcheiveStatus = async (
const respnse = await axios.put(
url,
{
- bookmarkStatus: status,
+ archeiveStatus: status,
},
{
headers: {
@@ -80,18 +90,16 @@ export const setOrgArcheiveStatus = async (
},
}
);
+
return respnse;
};
export const getUserOrgs = async (
authorizationToken: string,
username: string
-) => {
- const url =
- BACKEND_URL +
- '/api/protected/user/setArcheiveStatus/getUserOrgs/' +
- username;
- const respnse = await axios.get(url, {
+): Promise> => {
+ const url = BACKEND_URL + '/api/protected/user/getUserOrgs/' + username;
+ const respnse = await axios.get(url, {
headers: {
Accept: 'application/json',
Authorization: `Bearer ${authorizationToken}`,
diff --git a/src/app/components/buttonBar/index.tsx b/src/app/components/buttonBar/index.tsx
index 826c4f1..4945f7e 100644
--- a/src/app/components/buttonBar/index.tsx
+++ b/src/app/components/buttonBar/index.tsx
@@ -1,27 +1,39 @@
-import React from 'react';
+import React, { useState } from 'react';
import TimeRangeSwitch from 'app/components/timeRangeSwitch';
import './index.scss';
-const ButtonBar = () => {
+import { GetProject } from 'app/api/project';
+import { useNavigate } from 'react-router-dom';
+interface Props {
+ weekly: boolean;
+ setWeekly: (week: boolean) => void;
+ project: GetProject | null;
+ workspaceName: string | undefined;
+}
+const ButtonBar: React.FC = ({
+ weekly,
+ setWeekly,
+ project,
+ workspaceName,
+}) => {
+ const navigate = useNavigate();
return (
- ← Back
-
+ navigate(`/workspace/${workspaceName}`)}
+ >
+ ← Back
+
+
-
Appetizer
-
- Lorem ipsum dolor sit amet consectetur adipisicing elit. Libero,
- officiis aut. Expedita doloremque itaque quae perspiciatis ratione aut
- tempora mollitia obcaecati saepe. Quam sapiente odio molestias eos at
- nisi dolorum. At corporis dolore, explicabo et eveniet facilis amet sit
- reiciendis quae officia quam vel quisquam in blanditiis ad, id
- praesentium voluptates ipsum! Sunt magni placeat maiores libero non
- fugiat veniam? Voluptatibus blanditiis ipsum enim eum, exercitationem
- culpa quidem unde ex consectetur dignissimos quae libero cumque! Quis
- eveniet unde, necessitatibus sit corrupti eius saepe nobis magnam et
- tempore architecto libero dolorum?
-
+ {project && (
+ <>
+
{project.name}
+
{project.description}
+ >
+ )}
);
};
diff --git a/src/app/components/firstVisit/index.scss b/src/app/components/firstVisit/index.scss
new file mode 100644
index 0000000..c215fb5
--- /dev/null
+++ b/src/app/components/firstVisit/index.scss
@@ -0,0 +1,32 @@
+.modal-footer {
+ height: 100%;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ font-size: 0.5rem;
+ font-weight: 600;
+ a {
+ text-decoration: none;
+ }
+}
+
+.firstvisit-btn {
+ background-color: #402aa4;
+ border: none;
+ color: white;
+ padding: 0.5rem 1rem;
+ border-radius: 5rem;
+ border: 1px solid #402aa4;
+ font-size: 1rem;
+}
+
+.firstvisit-btn:hover {
+ background-color: whitesmoke;
+ border: none;
+ color: #402aa4;
+ padding: 0.5rem 1rem;
+ border-radius: 5rem;
+ border: 1px solid #402aa4;
+ font-size: 1rem;
+}
diff --git a/src/app/components/firstVisit/index.tsx b/src/app/components/firstVisit/index.tsx
new file mode 100644
index 0000000..8f6822a
--- /dev/null
+++ b/src/app/components/firstVisit/index.tsx
@@ -0,0 +1,42 @@
+import { useEffect, useState } from 'react';
+import Popup from '../popup';
+import './index.scss';
+
+export default function FirstVisit() {
+ const [show, setShow] = useState(false);
+ useEffect(() => {
+ const visit = localStorage.getItem('visit');
+ if (visit !== 'true') {
+ localStorage.setItem('visit', 'true');
+
+ setShow(true);
+ }
+ }, []);
+
+ return (
+ <>
+ {show && (
+ setShow(false)}
+ footer={
+
+
+ Google Summer of Code is a global, online program focused on
+ bringing new contributors into open source software development.
+
+
+ Visit GSOC
+
+
+ }
+ popup_zindex={{ overlay: 3000, modal: 4000 }}
+ />
+ )}
+ >
+ );
+}
diff --git a/src/app/components/popup/index.scss b/src/app/components/popup/index.scss
new file mode 100644
index 0000000..7ddab5a
--- /dev/null
+++ b/src/app/components/popup/index.scss
@@ -0,0 +1,70 @@
+.popup-overlay {
+ position: fixed;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background-color: rgba(0, 0, 0, 0.5);
+}
+
+.popup-modal {
+ position: fixed;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ background-color: white;
+ border-radius: 10px;
+ max-width: 500px;
+ width: 100%;
+ max-height: 67vh;
+ overflow-y: auto;
+ color: black;
+}
+
+.popup-title {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ border-bottom: 1px solid black;
+ padding: 0.5em 1em 0.5em 1em;
+ background-color: #402aa4;
+ color: white;
+}
+
+.popup-body {
+ text-align: center;
+ padding: 0.5em 1em 0.5em 1em;
+}
+
+.popup-footer {
+ display: flex;
+ flex-direction: row-reverse;
+ margin: 0.6em;
+}
+
+.close-btn {
+ background: none;
+ border: none;
+ padding: 0.1em;
+ color: white;
+}
+
+.modal-btn {
+ width: 30px;
+ height: 30px;
+ background-color: transparent;
+ border: none;
+ color: white;
+ border: 1px solid white;
+ border-radius: 50%;
+}
+
+.modal-btn:hover {
+ width: 30px;
+ height: 30px;
+ background-color: white;
+ border: none;
+ color: #402aa4;
+ border: 1px solid #402aa4;
+ border-radius: 50%;
+}
diff --git a/src/app/components/popup/index.tsx b/src/app/components/popup/index.tsx
new file mode 100644
index 0000000..49e05c4
--- /dev/null
+++ b/src/app/components/popup/index.tsx
@@ -0,0 +1,85 @@
+import './index.scss';
+import ReactPortal from '../reactPortal';
+import { ReactNode } from 'react';
+type _POPUP_PROPS = {
+ onClose?: () => void;
+ onSubmit?: () => void;
+ title: string | ReactNode;
+ type?: 'success' | 'error';
+ submit?: string;
+ content?: string | ReactNode;
+ footer?: ReactNode;
+ popup_zindex?: {
+ overlay: number;
+ modal: number;
+ };
+};
+
+const Popup = ({
+ title,
+ content,
+ type,
+ onClose,
+ onSubmit,
+ submit,
+ footer,
+ popup_zindex,
+}: _POPUP_PROPS) => {
+ return (
+
+
+
+
+ {typeof title === 'string' ? (
+
{title}
+ ) : (
+
{title}
+ )}
+ {onClose && (
+
+
+
+
+
+ )}
+
+ {content &&
{content}
}
+
+ {onSubmit && (
+
+ {submit ?? 'Okay'}
+
+ )}
+
{footer}
+
+
+
+ );
+};
+
+export default Popup;
diff --git a/src/app/components/reactPortal/index.tsx b/src/app/components/reactPortal/index.tsx
new file mode 100644
index 0000000..a855a0a
--- /dev/null
+++ b/src/app/components/reactPortal/index.tsx
@@ -0,0 +1,14 @@
+import { ReactNode, useEffect, useState } from 'react';
+import { createPortal } from 'react-dom';
+
+/**
+ * Ensures that document.body is available
+ */
+export default function ReactPortal({ children }: { children: ReactNode }) {
+ const [mounted, setMounted] = useState(false);
+ useEffect(() => {
+ setMounted(true);
+ }, []);
+
+ return <>{mounted && createPortal(<>{children}>, document.body)}>;
+}
diff --git a/src/app/components/search/index.tsx b/src/app/components/search/index.tsx
index 1c6e725..f568707 100644
--- a/src/app/components/search/index.tsx
+++ b/src/app/components/search/index.tsx
@@ -1,6 +1,6 @@
import React from 'react';
import { useDispatch } from 'react-redux';
-import { searchAction } from 'features/home/slices/projectSearchSlice';
+import { searchAction } from 'features/workspace/slices/projectSearchSlice';
import search_icon from 'app/assets/images/search_icon.svg';
import './index.scss';
@@ -12,7 +12,7 @@ const SearchBar = () => {
};
const handleSearch = (e: React.FormEvent) => {
e.preventDefault();
- if (search !== '') dispatch(searchAction(search));
+ dispatch(searchAction(search));
};
return (
diff --git a/src/app/components/timeRangeSwitch/index.scss b/src/app/components/timeRangeSwitch/index.scss
index 71d739e..52062b9 100644
--- a/src/app/components/timeRangeSwitch/index.scss
+++ b/src/app/components/timeRangeSwitch/index.scss
@@ -1,3 +1,10 @@
+.timerange-cont {
+ display: flex;
+ justify-content: center;
+ height: 100%;
+ align-items: center;
+}
+
.timerange-cont button {
border: none;
outline: none;
diff --git a/src/app/components/timeRangeSwitch/index.tsx b/src/app/components/timeRangeSwitch/index.tsx
index edfa50e..6e62dec 100644
--- a/src/app/components/timeRangeSwitch/index.tsx
+++ b/src/app/components/timeRangeSwitch/index.tsx
@@ -1,24 +1,34 @@
-import React from 'react';
+import React, { useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { timeRangeModel } from 'features/project/components/contributorCard/types';
import { weekAction, monthAction } from './timeRangeSlice';
import './index.scss';
+import { clearConfigCache } from 'prettier';
-const TimeRangeSwitch = () => {
- const dispatch = useDispatch();
- const isWeekly = useSelector((state: timeRangeModel) => state.isWeekly.value);
+interface Props {
+ weekly: boolean;
+ setWeekly: (bool: boolean) => void;
+}
+const TimeRangeSwitch: React.FC = ({ weekly, setWeekly }) => {
+ // const dispatch = useDispatch();
+ // const isWeekly = useSelector((state: timeRangeModel) => state.isWeekly.value);
+ useEffect(()=>{
+
+ },[weekly, setWeekly])
return (
dispatch(weekAction())}
- className={isWeekly ? 'active' : ''}
+ onClick={() => setWeekly(true)}
+ className={weekly ? 'active' : ''}
+ style={weekly ? {} : { background: 'transparent' }}
>
Weekly{' '}
dispatch(monthAction())}
- className={isWeekly ? '' : 'active'}
+ onClick={() => setWeekly(false)}
+ className={weekly ? '' : 'active'}
+ style={weekly ? { background: 'transparent' } : {}}
>
Monthly{' '}
diff --git a/src/app/constants/api.ts b/src/app/constants/api.ts
index 1dff1ff..a81f0db 100644
--- a/src/app/constants/api.ts
+++ b/src/app/constants/api.ts
@@ -1 +1 @@
-export const AVATAR_URL="https://api.multiavatar.com"
+export const AVATAR_URL = 'https://api.multiavatar.com';
diff --git a/src/app/context/user/userContext.tsx b/src/app/context/user/userContext.tsx
new file mode 100644
index 0000000..0d7da6e
--- /dev/null
+++ b/src/app/context/user/userContext.tsx
@@ -0,0 +1,13 @@
+import { UserOrgs } from 'app/api/user';
+import { createContext } from 'react';
+
+export interface UserContextType {
+ username: String | null;
+ setUsername: (name: String) => void;
+ userOrgs: UserOrgs | null;
+ setUserOrgs: (user_Orgs: UserOrgs) => void;
+}
+
+const UserContext = createContext
(undefined);
+
+export default UserContext;
diff --git a/src/app/context/user/userState.tsx b/src/app/context/user/userState.tsx
new file mode 100644
index 0000000..9648558
--- /dev/null
+++ b/src/app/context/user/userState.tsx
@@ -0,0 +1,22 @@
+import { FC, ReactNode, useState } from 'react';
+import UserContext from './userContext';
+import { UserOrgs } from 'app/api/user';
+
+interface Props {
+ children: ReactNode;
+}
+
+const UserState: FC = ({ children }) => {
+ const [username, setUsername] = useState(null);
+ const [userOrgs, setUserOrgs] = useState(null);
+
+ return (
+
+ {children}
+
+ );
+};
+
+export default UserState;
diff --git a/src/app/index.scss b/src/app/index.scss
index 058bdb6..f49efde 100644
--- a/src/app/index.scss
+++ b/src/app/index.scss
@@ -37,3 +37,8 @@ a:hover {
.bd-white {
border: 1px solid white;
}
+
+
+.pointer{
+ cursor: pointer;
+}
\ No newline at end of file
diff --git a/src/app/index.tsx b/src/app/index.tsx
index c71f936..3a9bf08 100644
--- a/src/app/index.tsx
+++ b/src/app/index.tsx
@@ -1,15 +1,52 @@
-import React from 'react';
import Navbar from 'app/components/navbar';
import BasicRoutes from 'app/routes/BasicRoutes';
import './index.scss';
-import { Toaster } from 'react-hot-toast';
+import toast, { Toaster } from 'react-hot-toast';
+import { getUser } from './api/user';
+import { useLocation, useNavigate } from 'react-router-dom';
+import { useQuery } from 'react-query';
+import { useContext } from 'react';
+import UserContext from './context/user/userContext';
+
function App() {
+ const navigate = useNavigate();
+ const location = useLocation();
+ const currentPath = location.pathname;
+ const userContext = useContext(UserContext);
+ const token = localStorage.getItem('token');
+ const checklogin = async () => {
+ if (token != null) {
+ try {
+ const userData = await getUser(token);
+ userContext?.setUsername(userData.data.message);
+ if (currentPath == '/login') {
+ navigate('/');
+ }
+ } catch (e) {
+ localStorage.removeItem('token');
+ if (currentPath != '/login') {
+ toast.error('Session expired');
+ navigate('/login');
+ }
+ }
+ } else {
+ if (currentPath != '/login') {
+ toast.error('Not authenticated');
+ navigate('/login');
+ }
+ }
+ };
+ const {} = useQuery('login', checklogin, {
+ enabled: true,
+ staleTime: Infinity,
+ });
+
return (
- <>
+
-
- >
+
+
);
}
diff --git a/src/app/routes/BasicRoutes.tsx b/src/app/routes/BasicRoutes.tsx
index d9f58ca..576a76f 100644
--- a/src/app/routes/BasicRoutes.tsx
+++ b/src/app/routes/BasicRoutes.tsx
@@ -1,22 +1,57 @@
-import React from 'react';
+
import { Routes, Route } from 'react-router-dom';
-import Home from 'features/home';
import ProjectPage from 'features/project';
import AddProject from 'features/AddProject';
import Error from 'features/Error';
import WorkspaceView from 'features/workspace-view';
import Login from 'features/login';
import AddWorkspace from 'features/AddWorkspace';
+import Workspace from 'features/workspace';
+
+import EditWorkspace from 'features/EditWorkspace';
+import EditProject from 'features/EditProject';
+import WorkspaceMembers from 'features/workspace-members ';
+import WorkspaceAddMember from 'features/WorkspaceAddMember';
+import ProjectMembers from 'features/project-members ';
+import ProjectAddMember from 'features/ProjectAddMember ';
+import FirstVisit from '../components/firstVisit';
+
const BasicRoutes = () => {
return (
- } />
- } />
- } />
- } />
+ } />
+ } />
+ }
+ />
+ } />
+ } />
} />
} />
+ } />
+ }
+ />
+ }
+ />
+ }
+ />
+ }
+ />
+ }
+ />
} />
+ {/* } /> */}
);
};
diff --git a/src/app/state/action-creators/orgActions.ts b/src/app/state/action-creators/orgActions.ts
index 6f3b308..6c7caa4 100644
--- a/src/app/state/action-creators/orgActions.ts
+++ b/src/app/state/action-creators/orgActions.ts
@@ -1,23 +1,17 @@
-import { Dispatch } from "redux"
-import { Organization } from "../reducers/orgReducers"
+import { Dispatch } from 'redux';
+import { Organization } from '../reducers/orgReducers';
+export const AddOrganization = (org: Organization) => {
+ return (dispatch: Dispatch) => {
+ dispatch({ type: 'add', payload: org });
+ };
+};
-
-
-
-export const AddOrganization= (org:Organization)=>{
- return(dispatch:Dispatch)=>{
- dispatch({ type:'add',
- payload:org})
- }
-}
-
-export const DeleteOrg=(org:Organization)=>{
- return(dispatch:Dispatch)=>{
- dispatch({
- type:'delete',
- payload:org
- })
- }
-}
-
+export const DeleteOrg = (org: Organization) => {
+ return (dispatch: Dispatch) => {
+ dispatch({
+ type: 'delete',
+ payload: org,
+ });
+ };
+};
diff --git a/src/app/state/action-creators/projectActions.ts b/src/app/state/action-creators/projectActions.ts
index 1f2018a..171e738 100644
--- a/src/app/state/action-creators/projectActions.ts
+++ b/src/app/state/action-creators/projectActions.ts
@@ -1,22 +1,23 @@
-import { Dispatch } from "redux"
+import { Dispatch } from 'redux';
+export const addProject = (
+ project: [string, { archieve: boolean; bookmark: boolean }]
+) => {
+ return (dispatch: Dispatch) => {
+ dispatch({
+ type: 'add',
+ payload: project,
+ });
+ };
+};
-export const addProject = (project:[string,{archieve:boolean, bookmark:boolean}])=>{
-
- return(dispatch:Dispatch)=>{
- dispatch({
- type: 'add',
- payload: project
- })
- }
-}
-
-export const deleteProject= (project:[string,{archeive:boolean, bookmark:boolean}])=>{
-
- return(dispatch:Dispatch)=>{
- dispatch({
- type:'delete',
- payload: project
- })
- }
-}
\ No newline at end of file
+export const deleteProject = (
+ project: [string, { archeive: boolean; bookmark: boolean }]
+) => {
+ return (dispatch: Dispatch) => {
+ dispatch({
+ type: 'delete',
+ payload: project,
+ });
+ };
+};
diff --git a/src/app/state/action-creators/usersActions.ts b/src/app/state/action-creators/usersActions.ts
index b00d4dc..e7078bc 100644
--- a/src/app/state/action-creators/usersActions.ts
+++ b/src/app/state/action-creators/usersActions.ts
@@ -17,7 +17,3 @@ export const setAllUsernames = (usernames: string[]) => {
});
};
};
-
-
-
-
diff --git a/src/app/state/reducers/index.ts b/src/app/state/reducers/index.ts
index 36506a4..564b80f 100644
--- a/src/app/state/reducers/index.ts
+++ b/src/app/state/reducers/index.ts
@@ -1,7 +1,7 @@
import { combineReducers } from 'redux';
import { setAllUsernamesReducer, setUsernameReducer } from './usersReducers';
import timeRangeReducer from 'app/components/timeRangeSwitch/timeRangeSlice';
-import searchReducer from 'features/home/slices/projectSearchSlice';
+import searchReducer from 'features/workspace/slices/projectSearchSlice';
import { orgReducer } from './orgReducers';
export const reducers = combineReducers({
@@ -9,7 +9,7 @@ export const reducers = combineReducers({
searchKeyword: searchReducer,
setUsername: setUsernameReducer,
setAllUsernames: setAllUsernamesReducer,
- organization: orgReducer
+ organization: orgReducer,
});
-export type RootState= ReturnType
+export type RootState = ReturnType;
diff --git a/src/app/state/reducers/orgReducers.ts b/src/app/state/reducers/orgReducers.ts
index c0689aa..a47ac3d 100644
--- a/src/app/state/reducers/orgReducers.ts
+++ b/src/app/state/reducers/orgReducers.ts
@@ -1,29 +1,25 @@
-import { act } from "react-dom/test-utils"
-
-export interface Organization{
- name: string,
- description: string|null
+import { act } from 'react-dom/test-utils';
+export interface Organization {
+ name: string;
+ description: string | null;
}
-
-export const orgReducer=(state: Organization[]=[], action:{
- type:string,
- payload: Organization
-})=>{
-
- switch(action.type){
- case 'add':
- return state.concat(action.payload)
-
- case 'delete':
- return state.filter(elem=>elem!=action.payload)
-
- default:
- return state
- }
-
-}
-
-
-
+export const orgReducer = (
+ state: Organization[] = [],
+ action: {
+ type: string;
+ payload: Organization;
+ }
+) => {
+ switch (action.type) {
+ case 'add':
+ return state.concat(action.payload);
+
+ case 'delete':
+ return state.filter((elem) => elem != action.payload);
+
+ default:
+ return state;
+ }
+};
diff --git a/src/app/state/store.ts b/src/app/state/store.ts
index 6471a84..a4660c2 100644
--- a/src/app/state/store.ts
+++ b/src/app/state/store.ts
@@ -1,6 +1,6 @@
import { legacy_createStore as createStore } from 'redux';
-import {reducers} from './reducers';
+import { reducers } from './reducers';
const store = createStore(reducers, {});
diff --git a/src/envConstants.ts b/src/envConstants.ts
index efcb05a..81d836e 100644
--- a/src/envConstants.ts
+++ b/src/envConstants.ts
@@ -1,3 +1,6 @@
-export const CLIENT_ID = '149d2857118e05e729a8';
-export const BACKEND_URL = 'http://localhost:8080';
-export const AVATAR_API= "w9zrqHdDa4MsYB";
\ No newline at end of file
+export const CLIENT_ID = process.env.CLIENT_ID;
+export const BACKEND_URL = process.env.BACKEND_URL;
+export const AVATAR_API = process.env.AVATAR_API;
+
+// http://13.233.127.61:8080
+// https://leaderboard-java.mdgspace.org
\ No newline at end of file
diff --git a/src/features/AddProject/index.tsx b/src/features/AddProject/index.tsx
index da553f2..b8f3464 100644
--- a/src/features/AddProject/index.tsx
+++ b/src/features/AddProject/index.tsx
@@ -1,61 +1,41 @@
-import React, { ChangeEvent, useState } from 'react';
+import React, { ChangeEvent, useContext, useEffect, useState } from 'react';
import './index.scss';
import tick from '../../app/assets/images/tick.png';
-import { useNavigate } from 'react-router-dom';
-import { getUser } from 'app/api/user';
+import { useNavigate, useParams } from 'react-router-dom';
import toast from 'react-hot-toast';
-import { useQuery } from 'react-query';
-import axios from 'axios';
import { addProject } from 'app/api/project';
+import { Projects, getOrgProjects } from 'app/api/organization';
+
+
const AddProject = () => {
const navigate = useNavigate();
const token = localStorage.getItem('token');
- const orgName = 'orgName';
+ const { spaceName } = useParams();
const [name, setName] = useState(null);
const [description, setDescription] = useState(null);
const [link, setLink] = useState(null);
- const [validLink, setValidLink] = useState(false);
+ const [orgProject, setOrgProjects] = useState(null);
- const [validName, setValidName] = useState(false);
-
- const checkLogin = async () => {
- if (token != null) {
- const userData = await getUser(token);
- return userData.data;
- } else {
- toast.error('Not authorized');
- navigate('/login');
+ const fetchData = async () => {
+ if (token && spaceName) {
+ try {
+ const res = await getOrgProjects(token, spaceName);
+ setOrgProjects(res.data.projects);
+ } catch (e) {}
}
};
- const { data, isError } = useQuery({
- queryFn: () => checkLogin(),
- queryKey: 'checkLogin',
- });
-
- if (isError) {
- toast.error('Session Expired');
- navigate('/login');
- }
+ useEffect(() => {
+ fetchData();
+ }, [spaceName]);
const linkChange = async (event: ChangeEvent) => {
setLink(event.target.value);
-
- if (isGitHubRepositoryLink(event.target.value)) {
- try {
- const response = await axios.get(event.target.value);
- setValidLink(true);
- return;
- } catch (e) {}
- }
-
- setValidLink(false);
};
const nameChange = async (event: ChangeEvent) => {
setName(event.target.value);
- setValidName(true);
};
function isGitHubRepositoryLink(link: string): boolean {
@@ -67,39 +47,50 @@ const AddProject = () => {
return githubRepoPattern.test(link);
}
+ function isValidName(input: string): boolean {
+ // Regular expression to match only alphanumeric characters, hyphens, and underscores
+ const regex = /^[a-zA-Z0-9-_]+$/;
+
+ // Test if the input string matches the regular expression
+ return regex.test(input);
+ }
+
+ const isUnique = (name: string) => {
+ if (orgProject && name in orgProject) {
+ return false;
+ }
+ return true;
+ };
+
const SubmitHandler = async () => {
if (
+ spaceName &&
token &&
name &&
- validName &&
- validLink &&
- description &&
link &&
- description?.length > 3
+ isValidName(name) &&
+ isGitHubRepositoryLink(link) &&
+ description &&
+ description?.length < 200
) {
- try {
- const res = await addProject(token, orgName, {
+ const func = async () => {
+ const res = await addProject(token, spaceName, {
name: name,
description: description,
link: link,
});
- } catch (e) {
- toast.error('Error while saving');
- }
+ navigate(`/workspace/${spaceName}`);
+ };
+ toast.promise(func(), {
+ loading: 'Saving Project',
+ success: Project saved ,
+ error: Could not save ,
+ });
+ } else {
+ toast.error('Invalid inputs');
}
-
- toast.error('Invalid inputs');
};
-
- toast.promise(
- SubmitHandler(),{
- loading: 'Saving Project',
- success: Project saved ,
- error:Could not save
- }
- )
-
return (
diff --git a/src/features/AddWorkspace/index.scss b/src/features/AddWorkspace/index.scss
new file mode 100644
index 0000000..f6f5ed5
--- /dev/null
+++ b/src/features/AddWorkspace/index.scss
@@ -0,0 +1,162 @@
+$breakpoint-tablet: 768px;
+.main_aworkspace_container {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ width: 100%;
+ height: max-content;
+ padding: 2rem;
+ .addworkspace-form-container {
+ background: linear-gradient(110.51deg, #141432 0.9%, #2a2a4b 101.51%);
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+ width: 100%;
+ height: 100%;
+ padding: 2rem 1rem;
+ border-radius: 1.25rem;
+ gap: 2rem;
+ .submit {
+ display: flex;
+ padding: 1rem 2rem;
+ align-self: flex-end;
+ font-size: 1.5rem;
+ border-radius: 0.875rem;
+ background: #402aa4;
+ color: white;
+ outline: none;
+ border: none;
+ }
+ }
+
+ .single-form-element-container {
+ display: flex;
+ flex-direction: column;
+ gap: 1rem;
+ width: 100%;
+ .label {
+ color: #8181ff;
+ font-size: 1.5rem;
+ font-weight: 900;
+ }
+ }
+}
+
+.custom-input {
+ box-sizing: border-box;
+ border: 0.8px solid #402aa4;
+ border-radius: 14px;
+ background-color: transparent;
+ align-items: flex-start;
+ color: rgba(173, 173, 255, 0.75);
+ padding: 1.5rem 1rem;
+ font-size: 1.25rem;
+ font-family: 'Poppins';
+ outline: none;
+}
+
+.custom-input::placeholder {
+ color: rgba(173, 173, 255, 0.75);
+ font-family: Poppins;
+ font-size: 1.25rem;
+ font-weight: 100;
+ line-height: normal;
+}
+
+.file-input-container {
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+ width: 100%;
+ max-width: 30rem;
+ padding: 2rem;
+ border-radius: 1.25rem;
+ background: linear-gradient(
+ 138deg,
+ rgba(20, 20, 50, 0.7) 5.19%,
+ rgba(20, 20, 50, 0.7) 97.11%
+ );
+ backdrop-filter: blur(25px);
+
+ .custom-file-input {
+ visibility: hidden;
+ }
+ .file-label {
+ width: fit-content;
+ padding: 0.4375rem 0.5625rem;
+ border-radius: 1.75rem;
+ background: #402aa4;
+ color: white;
+ }
+}
+
+.add-member-container {
+ position: relative;
+ box-sizing: border-box;
+ border: 0.8px solid #402aa4;
+ border-radius: 14px;
+ display: flex;
+ flex-direction: row;
+ outline: #141432;
+ .custom-input {
+ display: flex;
+ width: 100%;
+ border: none;
+ outline: none;
+ }
+ button {
+ border: none;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ font-size: 1.25rem;
+ margin: 0.75rem 1rem;
+ color: white;
+ width: 7rem;
+ font-weight: 100 !important;
+ border-radius: 2.3125rem;
+ background: #402aa4;
+ }
+}
+
+.added-members {
+ display: flex;
+ flex-direction: row;
+ gap: 2rem;
+ flex-wrap: wrap;
+ width: 100%;
+ .member-card {
+ display: inline-flex;
+ padding: 0.4375rem 0.875rem 0.625rem 0.875rem;
+ justify-content: center;
+ align-items: center;
+ gap: 0.5625rem;
+ color: #8989ce;
+ border-radius: 0.75rem;
+ background: #26264e;
+ .member-avatar {
+ width: 40px;
+ height: 40px;
+ }
+ .btn-cross {
+ appearance: none;
+ border: none;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ background-color: transparent;
+ }
+ }
+}
+
+@media (min-width: $breakpoint-tablet) {
+ .main_aworkspace_container {
+ padding: 2rem 5rem;
+ .addworkspace-form-container {
+ padding: 2rem 5rem;
+ }
+ }
+}
diff --git a/src/features/AddWorkspace/index.tsx b/src/features/AddWorkspace/index.tsx
index 92a1009..9a5814d 100644
--- a/src/features/AddWorkspace/index.tsx
+++ b/src/features/AddWorkspace/index.tsx
@@ -1,34 +1,32 @@
-import { getAllUser, getUser } from 'app/api/user';
-import React, { ChangeEvent, useState } from 'react';
+import { getAllUser } from 'app/api/user';
+import { ChangeEvent, useContext, useEffect, useState } from 'react';
import { useNavigate } from 'react-router-dom';
-import { useQuery } from 'react-query';
import toast from 'react-hot-toast';
-import { addOrg, getAllOrgs } from 'app/api/organization';
+import { addOrg, addOrgMembers, getAllOrgs } from 'app/api/organization';
import { uploadIcon } from 'app/api/file';
-import { useSelector } from 'react-redux';
-import { Organization } from 'app/state/reducers/orgReducers';
-import { RootState } from 'app/state/reducers';
-
+import './index.scss';
+import UserContext from 'app/context/user/userContext';
+import { AVATAR_URL } from 'app/constants/api';
+import { AVATAR_API } from 'envConstants';
const AddWorkspace = () => {
const navigate = useNavigate();
const token = localStorage.getItem('token');
+ const userContext = useContext(UserContext);
const [selectedFile, setSelectedFile] = useState
(null);
const [name, SetName] = useState(null);
const [description, setDiscription] = useState(null);
+ const [validDescription, setValidDescription] = useState(true);
const [validName, setValidName] = useState(false);
-
+ const [uniqueName, setUniqueName] = useState(false);
const [members, setMembers] = useState([]);
- const [memberName, setMemberName]=useState(null);
+ const [memberName, setMemberName] = useState(null);
const [users, setUsers] = useState([]);
const [orgs, setOrgs] = useState([]);
- const orgState= useSelector((state:RootState)=>state.organization);
-
-
const dataFetch = async () => {
try {
if (token) {
@@ -50,30 +48,9 @@ const AddWorkspace = () => {
} catch (e) {}
};
- const checklogin = async () => {
- if (token != null) {
- const userData = await getUser(token);
- return userData.data
- } else {
- toast.error("Not authorized")
- navigate('/login');
- }
- };
-
- const { data,isError } = useQuery({
- queryFn: () => checklogin(),
- queryKey: 'checkLogin',
- });
-
- if (isError) {
- toast.error("Session expired")
- navigate('/login');
- }
-
- const {} = useQuery({
- queryFn: () => dataFetch(),
- queryKey: 'allUsersAndAllOrgs',
- });
+ useEffect(() => {
+ dataFetch();
+ }, []);
const allowedFieTypes = ['image/jpeg', 'image/jpg', 'image/png'];
@@ -92,115 +69,213 @@ const AddWorkspace = () => {
function valid_name(str: string): boolean {
// Define a regular expression for special characters (excluding letters, digits, and spaces)
- const specialCharacters = /[^a-zA-Z0-9\s]/;
+ const specialCharacters = /^[a-zA-Z0-9_-]+$/;
// Check if the string contains any special characters
- return (
- specialCharacters.test(str) &&
- !str.endsWith('/userspace') &&
- !orgs.includes(str)
-
- );
+ return specialCharacters.test(str) && !str.endsWith('-userspace');
}
- const isNotOrgName= (orgName:string)=>{
- orgState.forEach(el=>{
- if(el.name==orgName){
- return false
- }
- })
- return true
+ function isUniqueName(str: string): boolean {
+ return !orgs.includes(str);
}
const handleNameChange = (event: ChangeEvent) => {
SetName(event.target.value);
-
-
-
- setValidName(() => valid_name(event.target.value)&&isNotOrgName(event.target.value));
+ setUniqueName(() => isUniqueName(event.target.value));
+ setValidName(() => valid_name(event.target.value));
};
const handleDesriptionChange = (event: ChangeEvent) => {
setDiscription(event.target.value);
+ if (description?.length) {
+ setValidDescription(description.length < 200);
+ }
};
- const addMembers= ()=>{
- if(memberName){
- if(users.includes(memberName)&& memberName!=data?.message){
- setMembers([...members,memberName])
- setMemberName(null)
+ const addMembers = () => {
+ if (memberName) {
+ if (
+ users.includes(memberName) &&
+ memberName != userContext?.username &&
+ !members.includes(memberName)
+ ) {
+ setMembers([...members, memberName]);
+ setMemberName(null);
}
}
- }
-
+ };
- const SubmitHandler=async():Promise=>{
+ const removeMembers = (member: string) => {
+ const indexToRemove = members.indexOf(member);
+ if (indexToRemove !== -1) {
+ const updatedMembers = [
+ ...members.slice(0, indexToRemove),
+ ...members.slice(indexToRemove + 1),
+ ];
- if(validName&&description&&token&&name){
-
- const func= async():Promise=>{
- const dataRes= await addOrg(token,{
- name:name,
- description:description
- })
-
- try{
- if(selectedFile!=null){
- const fileRes= uploadIcon(token, name, selectedFile)
- }}catch(e){
-
- }
- navigate("/workspace-view")
- }
-
- toast.promise(
- func(),{
- loading:'Saving...',
- success: Workspace Saved ,
- error: Could not save. ,
+ setMembers(updatedMembers);
+ } else {
+ console.warn(`Member "${member}" not found in the members array.`);
+ }
+ };
+ const SubmitHandler = async (): Promise => {
+ if (
+ description &&
+ token &&
+ name &&
+ validName &&
+ uniqueName &&
+ validDescription
+ ) {
+ const func = async (): Promise => {
+ const dataRes = await addOrg(token, {
+ name: name,
+ description: description,
+ });
+ try {
+ if (selectedFile != null) {
+ const fileRes = uploadIcon(token, name, selectedFile);
}
- )
-
-
- }else{
- toast.error('Invalid inputs')
+ } catch (e) {}
+ if (members.length > 0) {
+ try {
+ const addMmebersRes = await addOrgMembers(token, name, members);
+ } catch (e) {}
+ }
+ navigate('/');
+ };
+
+ toast.promise(func(), {
+ loading: 'Saving Workspace',
+ success: Workspace saved ,
+ error: Could not save ,
+ });
+ } else {
+ toast.error('Invalid inputs');
}
-
-
- }
-
-
-
+ };
return (
-
-
-
-
Selected File: {selectedFile?.name}
-
-
-
-
)=>{setMemberName(e.target.value)}}
- placeholder='Enter membername'
- />
-
Add Memeber
-
{members}
-
Submit
+
+
+
+
Add Icon
+
+
+ Choose image files here
+
+
+
Supported formats: JPEG, JPG, PNG
+
Selected File: {selectedFile?.name}
+
+
+
+
+ Name
+
+
+ {!name ?
Name feild should not be empty
: <>>}
+ {!validName && name ?
Not a valid name
: <>>}
+ {!uniqueName && name ? (
+
Name already taken. Try another name
+ ) : (
+ <>>
+ )}
+
+
+
+ Description
+
+
+ {!description ?
Description feild should not be empty
: <>>}
+ {!validDescription ? (
+
Characters length should be less than 200
+ ) : (
+ <>>
+ )}
+
+ ) => {
+ setMemberName(e.target.value);
+ }}
+ placeholder='Github ID of user'
+ />
+
+ {'+ Add'}
+
+
+
+
+ {members.map((member, index) => {
+ return (
+
+
{' '}
+
{member}
{' '}
+
{
+ removeMembers(member);
+ }}
+ className='btn-cross'
+ >
+
+
+ {' '}
+
+
+ );
+ })}
+
+
+
+ Done
+
+
);
};
diff --git a/src/features/EditProject/index.scss b/src/features/EditProject/index.scss
new file mode 100644
index 0000000..f1e4f07
--- /dev/null
+++ b/src/features/EditProject/index.scss
@@ -0,0 +1,75 @@
+.add-project-container {
+ width: 70vw;
+ height: 76.5vh;
+ margin: 60px auto;
+ background: linear-gradient(110.51deg, #141432 0.9%, #2a2a4b 101.51%);
+ border-radius: 20px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ position: relative;
+}
+
+.add-project-form input {
+ box-sizing: border-box;
+
+ /* Auto layout */
+ border: 0.8px solid #402aa4;
+ border-radius: 14px;
+ background: linear-gradient(110.51deg, #141432 0.9%, #2a2a4b 101.51%);
+ display: flex;
+ flex-direction: row;
+ align-items: flex-start;
+ padding: 20px 100px 20px 30px;
+ margin: 20px 0;
+ font-family: 'Poppins';
+ font-style: normal;
+ font-weight: 400;
+ font-size: 20px;
+ line-height: 30px;
+ /* identical to box height */
+
+ color: rgba(173, 173, 255, 0.75);
+}
+
+.input-title {
+ font-family: 'Poppins';
+ font-style: normal;
+ font-weight: 700;
+ font-size: 20px;
+ line-height: 30px;
+ /* identical to box height */
+
+ color: #8181ff;
+}
+
+.add-project-btn {
+ position: absolute;
+ right: 66px;
+ bottom: 44px;
+ font-family: 'Poppins';
+ font-style: normal;
+ font-weight: 400;
+ font-size: 15px;
+ line-height: 22px;
+ /* identical to box height */
+
+ display: flex;
+ align-items: center;
+ display: flex;
+ flex-direction: row;
+ align-items: flex-start;
+ padding: 10px 16px;
+ gap: 2px;
+ flex: none;
+ order: 1;
+ flex-grow: 0;
+ background: #402aa4;
+ border-radius: 14px;
+ color: #ffffff;
+}
+
+.add-project-btn img {
+ padding-top: 3px;
+ padding-right: 4px;
+}
diff --git a/src/features/EditProject/index.tsx b/src/features/EditProject/index.tsx
new file mode 100644
index 0000000..3fc9ad5
--- /dev/null
+++ b/src/features/EditProject/index.tsx
@@ -0,0 +1,154 @@
+import { ChangeEvent, useEffect, useState } from 'react';
+import './index.scss';
+import tick from '../../app/assets/images/tick.png';
+import { useNavigate, useParams } from 'react-router-dom';
+import toast from 'react-hot-toast';
+import {
+ getProject,
+ updateProject,
+} from 'app/api/project';
+import UserContext from 'app/context/user/userContext';
+import { Projects, getOrgProjects } from 'app/api/organization';
+
+
+
+const EditProject = () => {
+ const navigate = useNavigate();
+ const token = localStorage.getItem('token');
+ const [name, setName] = useState
(null);
+ const [description, setDescription] = useState(null);
+ const [link, setLink] = useState(null);
+ const [orgProject, setOrgProjects] = useState(null);
+
+ const { spaceName, projectName } = useParams();
+
+ const fetchData = async () => {
+ if (token && spaceName && projectName) {
+ try {
+ const res = await getOrgProjects(token, spaceName);
+ setOrgProjects(res.data.projects);
+ const proRes = await getProject(token, projectName, spaceName);
+ setName(projectName);
+ setDescription(proRes.data.description);
+ setLink(proRes.data.link);
+ } catch (e) {}
+ }
+ };
+
+ useEffect(() => {
+ fetchData();
+ }, [spaceName]);
+
+ const linkChange = async (event: ChangeEvent) => {
+ setLink(event.target.value);
+ };
+
+ const nameChange = async (event: ChangeEvent) => {
+ setName(event.target.value);
+ };
+
+ function isGitHubRepositoryLink(link: string): boolean {
+ // Define a regular expression pattern for a GitHub repository URL
+ const githubRepoPattern =
+ /^https:\/\/github\.com\/[a-zA-Z0-9-]+\/[a-zA-Z0-9-]+(\.git)?$/;
+
+ // Check if the provided link matches the pattern
+ return githubRepoPattern.test(link);
+ }
+
+ function isValidName(input: string): boolean {
+ // Regular expression to match only alphanumeric characters, hyphens, and underscores
+ const regex = /^[a-zA-Z0-9-_]+$/;
+
+ // Test if the input string matches the regular expression
+ return regex.test(input);
+ }
+
+ const isUnique = (name: string) => {
+ if (orgProject && name in orgProject) {
+ return false;
+ }
+ return true;
+ };
+
+ const SubmitHandler = async () => {
+ if (
+ spaceName &&
+ token &&
+ name &&
+ link &&
+ projectName &&
+ isValidName(name) &&
+ isGitHubRepositoryLink(link) &&
+ description &&
+ description?.length < 200
+ ) {
+ const func = async () => {
+ const res = await updateProject(token, projectName, spaceName, {
+ name: name,
+ description: description,
+ link: link,
+ });
+ navigate(`/workspace/${spaceName}`);
+ };
+ toast.promise(func(), {
+ loading: 'Saving Project',
+ success: Project saved ,
+ error: Could not save ,
+ });
+ } else {
+ toast.error('Invalid inputs');
+ }
+ };
+
+ return (
+
+
+
+
+ Done
+
+
+
+ );
+};
+
+export default EditProject;
diff --git a/src/features/EditWorkspace/index.scss b/src/features/EditWorkspace/index.scss
new file mode 100644
index 0000000..f6f5ed5
--- /dev/null
+++ b/src/features/EditWorkspace/index.scss
@@ -0,0 +1,162 @@
+$breakpoint-tablet: 768px;
+.main_aworkspace_container {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ width: 100%;
+ height: max-content;
+ padding: 2rem;
+ .addworkspace-form-container {
+ background: linear-gradient(110.51deg, #141432 0.9%, #2a2a4b 101.51%);
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+ width: 100%;
+ height: 100%;
+ padding: 2rem 1rem;
+ border-radius: 1.25rem;
+ gap: 2rem;
+ .submit {
+ display: flex;
+ padding: 1rem 2rem;
+ align-self: flex-end;
+ font-size: 1.5rem;
+ border-radius: 0.875rem;
+ background: #402aa4;
+ color: white;
+ outline: none;
+ border: none;
+ }
+ }
+
+ .single-form-element-container {
+ display: flex;
+ flex-direction: column;
+ gap: 1rem;
+ width: 100%;
+ .label {
+ color: #8181ff;
+ font-size: 1.5rem;
+ font-weight: 900;
+ }
+ }
+}
+
+.custom-input {
+ box-sizing: border-box;
+ border: 0.8px solid #402aa4;
+ border-radius: 14px;
+ background-color: transparent;
+ align-items: flex-start;
+ color: rgba(173, 173, 255, 0.75);
+ padding: 1.5rem 1rem;
+ font-size: 1.25rem;
+ font-family: 'Poppins';
+ outline: none;
+}
+
+.custom-input::placeholder {
+ color: rgba(173, 173, 255, 0.75);
+ font-family: Poppins;
+ font-size: 1.25rem;
+ font-weight: 100;
+ line-height: normal;
+}
+
+.file-input-container {
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+ width: 100%;
+ max-width: 30rem;
+ padding: 2rem;
+ border-radius: 1.25rem;
+ background: linear-gradient(
+ 138deg,
+ rgba(20, 20, 50, 0.7) 5.19%,
+ rgba(20, 20, 50, 0.7) 97.11%
+ );
+ backdrop-filter: blur(25px);
+
+ .custom-file-input {
+ visibility: hidden;
+ }
+ .file-label {
+ width: fit-content;
+ padding: 0.4375rem 0.5625rem;
+ border-radius: 1.75rem;
+ background: #402aa4;
+ color: white;
+ }
+}
+
+.add-member-container {
+ position: relative;
+ box-sizing: border-box;
+ border: 0.8px solid #402aa4;
+ border-radius: 14px;
+ display: flex;
+ flex-direction: row;
+ outline: #141432;
+ .custom-input {
+ display: flex;
+ width: 100%;
+ border: none;
+ outline: none;
+ }
+ button {
+ border: none;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ font-size: 1.25rem;
+ margin: 0.75rem 1rem;
+ color: white;
+ width: 7rem;
+ font-weight: 100 !important;
+ border-radius: 2.3125rem;
+ background: #402aa4;
+ }
+}
+
+.added-members {
+ display: flex;
+ flex-direction: row;
+ gap: 2rem;
+ flex-wrap: wrap;
+ width: 100%;
+ .member-card {
+ display: inline-flex;
+ padding: 0.4375rem 0.875rem 0.625rem 0.875rem;
+ justify-content: center;
+ align-items: center;
+ gap: 0.5625rem;
+ color: #8989ce;
+ border-radius: 0.75rem;
+ background: #26264e;
+ .member-avatar {
+ width: 40px;
+ height: 40px;
+ }
+ .btn-cross {
+ appearance: none;
+ border: none;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ background-color: transparent;
+ }
+ }
+}
+
+@media (min-width: $breakpoint-tablet) {
+ .main_aworkspace_container {
+ padding: 2rem 5rem;
+ .addworkspace-form-container {
+ padding: 2rem 5rem;
+ }
+ }
+}
diff --git a/src/features/EditWorkspace/index.tsx b/src/features/EditWorkspace/index.tsx
new file mode 100644
index 0000000..f708b96
--- /dev/null
+++ b/src/features/EditWorkspace/index.tsx
@@ -0,0 +1,212 @@
+import { getAllUser, getUser } from 'app/api/user';
+import { ChangeEvent, useContext, useEffect, useState } from 'react';
+import { useNavigate, useParams } from 'react-router-dom';
+import toast from 'react-hot-toast';
+import {
+
+ getAllOrgs,
+ getOrg,
+ updateOrg,
+} from 'app/api/organization';
+import { uploadIcon } from 'app/api/file';
+
+import './index.scss';
+import UserContext from 'app/context/user/userContext';
+
+const EditWorkspace = () => {
+ const navigate = useNavigate();
+ const token = localStorage.getItem('token');
+ const { spaceName } = useParams();
+
+ const [selectedFile, setSelectedFile] = useState(null);
+ const [name, SetName] = useState(null);
+ const [description, setDiscription] = useState(null);
+ const [validDescription, setValidDescription] = useState(true);
+ const [validName, setValidName] = useState(false);
+ const [uniqueName, setUniqueName] = useState(false);
+
+
+ const [users, setUsers] = useState([]);
+ const [orgs, setOrgs] = useState([]);
+
+ const dataFetch = async () => {
+ try {
+ if (token && spaceName) {
+ const users_aray: string[] = [];
+ const org_aray: string[] = [];
+ const allUser = await getAllUser(token);
+ const allOrgs = await getAllOrgs(token);
+ allUser.data.users.forEach((user) => {
+ users_aray.push(user.username);
+ });
+
+ allOrgs.data.organizations.forEach((org) => {
+ org_aray.push(org.name);
+ });
+
+ setUsers(users_aray);
+ setOrgs(org_aray);
+ }
+ if (token && spaceName) {
+ SetName(spaceName);
+ setUniqueName(true);
+ setValidName(true);
+ }
+ if (token && spaceName) {
+ const Org = await getOrg(token, spaceName);
+ setDiscription(Org.data.description);
+ }
+ } catch (e) {}
+ };
+
+ useEffect(() => {
+ dataFetch();
+ }, []);
+
+ const allowedFieTypes = ['image/jpeg', 'image/jpg', 'image/png'];
+
+ const handleFileChange = (event: ChangeEvent) => {
+ const file = event.target.files?.[0];
+
+ if (file) {
+ if (allowedFieTypes.includes(file.type)) {
+ setSelectedFile(file);
+ } else {
+ setSelectedFile(null);
+ toast.error('Invalid file type');
+ }
+ }
+ };
+
+ function valid_name(str: string): boolean {
+ // Define a regular expression for special characters (excluding letters, digits, and spaces)
+ const specialCharacters = /^[a-zA-Z0-9_-]+$/;
+
+ // Check if the string contains any special characters
+ return specialCharacters.test(str) && !str.endsWith('-userspace');
+ }
+
+ function isUniqueName(str: string): boolean {
+ return !orgs.includes(str);
+ }
+
+ const handleNameChange = (event: ChangeEvent) => {
+ SetName(event.target.value);
+ setUniqueName(() => isUniqueName(event.target.value));
+ setValidName(() => valid_name(event.target.value));
+ };
+
+ const handleDesriptionChange = (event: ChangeEvent) => {
+ setDiscription(event.target.value);
+ if (description?.length) {
+ setValidDescription(description.length < 200);
+ }
+ };
+
+ const SubmitHandler = async (): Promise => {
+ if (
+ description &&
+ token &&
+ name &&
+ spaceName &&
+ validName &&
+ uniqueName &&
+ validDescription
+ ) {
+ const func = async (): Promise => {
+ const dataRes = await updateOrg(token, spaceName, {
+ name: name,
+ description: description,
+ });
+
+ try {
+ if (selectedFile != null) {
+ const fileRes = uploadIcon(token, name, selectedFile);
+ }
+ } catch (e) {}
+
+ navigate('/');
+ };
+
+ toast.promise(func(), {
+ loading: 'Saving Workspace',
+ success: Workspace saved ,
+ error: Could not save ,
+ });
+ } else {
+ toast.error('Invalid inputs');
+ }
+ };
+
+ return (
+
+
+
+
Add Icon
+
+
+ Choose image files here
+
+
+
Supported formats: JPEG, JPG, PNG
+
Selected File: {selectedFile?.name}
+
+
+
+
+ Name
+
+
+ {!name ?
Name feild should not be empty
: <>>}
+ {name != spaceName && !validName && name ? (
+
Not a valid name
+ ) : (
+ <>>
+ )}
+ {name != spaceName && !uniqueName && name ? (
+
Name already taken. Try another name
+ ) : (
+ <>>
+ )}
+
+
+
+ Description
+
+
+ {!description ?
Description feild should not be empty
: <>>}
+ {!validDescription ? (
+
Characters length should be less than 200
+ ) : (
+ <>>
+ )}
+
+
+
+ Done
+
+
+
+ );
+};
+
+export default EditWorkspace;
diff --git a/src/features/ProjectAddMember /index.scss b/src/features/ProjectAddMember /index.scss
new file mode 100644
index 0000000..e47491f
--- /dev/null
+++ b/src/features/ProjectAddMember /index.scss
@@ -0,0 +1,189 @@
+$breakpoint-tablet: 768px;
+.main_aworkspace_container {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ width: 100%;
+ height: max-content;
+ padding: 2rem;
+ .addworkspace-form-container {
+ background: linear-gradient(110.51deg, #141432 0.9%, #2a2a4b 101.51%);
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+ width: 100%;
+ height: 100%;
+ padding: 2rem 1rem;
+ border-radius: 1.25rem;
+ gap: 2rem;
+ .submit {
+ display: flex;
+ padding: 1rem 2rem;
+ align-self: flex-end;
+ font-size: 1.5rem;
+ border-radius: 0.875rem;
+ background: #402aa4;
+ color: white;
+ outline: none;
+ border: none;
+ }
+ }
+
+ .single-form-element-container {
+ display: flex;
+ position: relative;
+ flex-direction: column;
+ gap: 1rem;
+ width: 100%;
+ .member-search-result {
+ position: absolute;
+ border: 2px solid #8989ce;
+ top: 6rem;
+ background-color: #1414325b;
+ gap: 1rem;
+ width: fit-content;
+ backdrop-filter: blur(25px);
+ display: flex;
+ flex-direction: column;
+ padding: 1rem 1rem;
+ z-index: 1000;
+ p {
+ color: #8989ce;
+ width: fit-content;
+ font-size: 1.25rem;
+ font-weight: 900;
+ padding: 0.1rem 1rem;
+ border-radius: 1rem;
+ }
+ P:hover {
+ color: #8181ff;
+ cursor: pointer;
+ background-color: black;
+ }
+ }
+ .label {
+ color: #8181ff;
+ font-size: 1.5rem;
+ font-weight: 900;
+ }
+ }
+}
+
+.custom-input {
+ box-sizing: border-box;
+ border: 0.8px solid #402aa4;
+ border-radius: 14px;
+ background-color: transparent;
+ align-items: flex-start;
+ color: rgba(173, 173, 255, 0.75);
+ padding: 1.5rem 1rem;
+ font-size: 1.25rem;
+ font-family: 'Poppins';
+ outline: none;
+}
+
+.custom-input::placeholder {
+ color: rgba(173, 173, 255, 0.75);
+ font-family: Poppins;
+ font-size: 1.25rem;
+ font-weight: 100;
+ line-height: normal;
+}
+
+.file-input-container {
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+ width: 100%;
+ max-width: 30rem;
+ padding: 2rem;
+ border-radius: 1.25rem;
+ background: linear-gradient(
+ 138deg,
+ rgba(20, 20, 50, 0.7) 5.19%,
+ rgba(20, 20, 50, 0.7) 97.11%
+ );
+ backdrop-filter: blur(25px);
+
+ .custom-file-input {
+ visibility: hidden;
+ }
+ .file-label {
+ width: fit-content;
+ padding: 0.4375rem 0.5625rem;
+ border-radius: 1.75rem;
+ background: #402aa4;
+ color: white;
+ }
+}
+
+.add-member-container {
+ position: relative;
+ box-sizing: border-box;
+ border: 0.8px solid #402aa4;
+ border-radius: 14px;
+ display: flex;
+ flex-direction: row;
+ outline: #141432;
+ .custom-input {
+ display: flex;
+ width: 100%;
+ border: none;
+ outline: none;
+ }
+ button {
+ border: none;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ font-size: 1.25rem;
+ margin: 0.75rem 1rem;
+ color: white;
+ width: 7rem;
+ font-weight: 100 !important;
+ border-radius: 2.3125rem;
+ background: #402aa4;
+ }
+}
+
+.added-members {
+ display: flex;
+ flex-direction: row;
+ gap: 2rem;
+ flex-wrap: wrap;
+ width: 100%;
+ .member-card {
+ display: inline-flex;
+ padding: 0.4375rem 0.875rem 0.625rem 0.875rem;
+ justify-content: center;
+ align-items: center;
+ gap: 0.5625rem;
+ color: #8989ce;
+ border-radius: 0.75rem;
+ background: #26264e;
+ .member-avatar {
+ width: 40px;
+ height: 40px;
+ }
+ .btn-cross {
+ appearance: none;
+ border: none;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ background-color: transparent;
+ }
+ }
+}
+
+@media (min-width: $breakpoint-tablet) {
+ .main_aworkspace_container {
+ padding: 2rem 5rem;
+ .addworkspace-form-container {
+ padding: 2rem 5rem;
+ }
+ }
+}
diff --git a/src/features/ProjectAddMember /index.tsx b/src/features/ProjectAddMember /index.tsx
new file mode 100644
index 0000000..b08ff09
--- /dev/null
+++ b/src/features/ProjectAddMember /index.tsx
@@ -0,0 +1,194 @@
+
+import { ChangeEvent, useContext, useEffect, useState } from 'react';
+import { useNavigate, useParams } from 'react-router-dom';
+import { AVATAR_API } from 'envConstants';
+import { AVATAR_URL } from 'app/constants/api';
+import toast from 'react-hot-toast';
+import {
+ getOrgMembers,
+} from 'app/api/organization';
+
+import './index.scss';
+import UserContext from 'app/context/user/userContext';
+import { addProjectsMembers, getMembers } from 'app/api/project';
+
+const ProjectAddMember = () => {
+ const navigate = useNavigate();
+ const token = localStorage.getItem('token');
+ const userContext = useContext(UserContext);
+
+ const [members, setMembers] = useState([]);
+ const [memberName, setMemberName] = useState(null);
+
+ const { spaceName, projectName } = useParams();
+ const [projectMembers, setProjectMembers] = useState([]);
+ const [orgMembers, setOrgMembers] = useState([
+ 'kituuu',
+ 'karthik',
+ 'kartik',
+ 'kartar',
+ ]);
+ const [searchedMembers, setSearchedMembers] = useState([]);
+
+ const filterMembers = (search: string) => {
+ let temp: string[] = [];
+ orgMembers.forEach((member) => {
+ if (member.includes(search)) {
+ temp.push(member);
+ }
+ });
+
+ setSearchedMembers(temp);
+ };
+
+ const dataFetch = async () => {
+ try {
+ if (token && spaceName && projectName) {
+ const projectMemRes = await getMembers(token, projectName, spaceName);
+ const orgMemRes = await getOrgMembers(token, spaceName);
+
+ setProjectMembers(Object.keys(projectMemRes.data.members));
+ setOrgMembers(Object.keys(orgMemRes.data.members));
+ }
+ } catch (e) {}
+ };
+
+ useEffect(() => {
+ dataFetch();
+ }, [memberName]);
+
+ const addMembers = () => {
+ if (memberName) {
+ if (
+ orgMembers.includes(memberName) &&
+ !members.includes(memberName) &&
+ !projectMembers.includes(memberName)
+ ) {
+ setMembers([...members, memberName]);
+ setMemberName(null);
+ } else if (projectMembers.includes(memberName)) {
+ toast.error('Member already exists in the project');
+ }
+ }
+ };
+
+ const removeMembers = (member: string) => {
+ const indexToRemove = members.indexOf(member);
+
+ if (indexToRemove !== -1) {
+ const updatedMembers = [
+ ...members.slice(0, indexToRemove),
+ ...members.slice(indexToRemove + 1),
+ ];
+
+ setMembers(updatedMembers);
+ } else {
+ console.warn(`Member "${member}" not found in the members array.`);
+ }
+ };
+ const SubmitHandler = async (): Promise => {
+ if (token) {
+ const func = async (): Promise => {
+ if (members.length > 0 && spaceName && projectName) {
+ try {
+ const addMmebersRes = await addProjectsMembers(
+ token,
+ projectName,
+ spaceName,
+ members
+ );
+ } catch (e) {}
+ }
+ navigate(`/projectMembers/${spaceName}/${projectName}`);
+ };
+
+ toast.promise(func(), {
+ loading: 'On Progress',
+ success: Members added ,
+ error: Could not add ,
+ });
+ } else {
+ toast.error('Invalid inputs');
+ }
+ };
+
+ return (
+
+
+
+
+ ) => {
+ setMemberName(e.target.value);
+ if (memberName) filterMembers(memberName);
+ }}
+ placeholder='Github ID of user'
+ />
+
+ {'+ Add'}
+
+
+ {memberName && searchedMembers.length > 0 && (
+
+ {searchedMembers.map((member, index) => {
+ return (
+
{
+ setMemberName(member);
+ setSearchedMembers([]);
+ }}
+ key={index}
+ >
+ {member}
+
+ );
+ })}
+
+ )}
+
+
+ {members.map((member, index) => {
+ return (
+
+
{' '}
+
{member}
{' '}
+
{
+ removeMembers(member);
+ }}
+ className='btn-cross'
+ >
+
+
+ {' '}
+
+
+ );
+ })}
+
+
+
+ Done
+
+
+
+ );
+};
+
+export default ProjectAddMember;
diff --git a/src/features/WorkspaceAddMember/index.scss b/src/features/WorkspaceAddMember/index.scss
new file mode 100644
index 0000000..f6f5ed5
--- /dev/null
+++ b/src/features/WorkspaceAddMember/index.scss
@@ -0,0 +1,162 @@
+$breakpoint-tablet: 768px;
+.main_aworkspace_container {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ width: 100%;
+ height: max-content;
+ padding: 2rem;
+ .addworkspace-form-container {
+ background: linear-gradient(110.51deg, #141432 0.9%, #2a2a4b 101.51%);
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+ width: 100%;
+ height: 100%;
+ padding: 2rem 1rem;
+ border-radius: 1.25rem;
+ gap: 2rem;
+ .submit {
+ display: flex;
+ padding: 1rem 2rem;
+ align-self: flex-end;
+ font-size: 1.5rem;
+ border-radius: 0.875rem;
+ background: #402aa4;
+ color: white;
+ outline: none;
+ border: none;
+ }
+ }
+
+ .single-form-element-container {
+ display: flex;
+ flex-direction: column;
+ gap: 1rem;
+ width: 100%;
+ .label {
+ color: #8181ff;
+ font-size: 1.5rem;
+ font-weight: 900;
+ }
+ }
+}
+
+.custom-input {
+ box-sizing: border-box;
+ border: 0.8px solid #402aa4;
+ border-radius: 14px;
+ background-color: transparent;
+ align-items: flex-start;
+ color: rgba(173, 173, 255, 0.75);
+ padding: 1.5rem 1rem;
+ font-size: 1.25rem;
+ font-family: 'Poppins';
+ outline: none;
+}
+
+.custom-input::placeholder {
+ color: rgba(173, 173, 255, 0.75);
+ font-family: Poppins;
+ font-size: 1.25rem;
+ font-weight: 100;
+ line-height: normal;
+}
+
+.file-input-container {
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+ width: 100%;
+ max-width: 30rem;
+ padding: 2rem;
+ border-radius: 1.25rem;
+ background: linear-gradient(
+ 138deg,
+ rgba(20, 20, 50, 0.7) 5.19%,
+ rgba(20, 20, 50, 0.7) 97.11%
+ );
+ backdrop-filter: blur(25px);
+
+ .custom-file-input {
+ visibility: hidden;
+ }
+ .file-label {
+ width: fit-content;
+ padding: 0.4375rem 0.5625rem;
+ border-radius: 1.75rem;
+ background: #402aa4;
+ color: white;
+ }
+}
+
+.add-member-container {
+ position: relative;
+ box-sizing: border-box;
+ border: 0.8px solid #402aa4;
+ border-radius: 14px;
+ display: flex;
+ flex-direction: row;
+ outline: #141432;
+ .custom-input {
+ display: flex;
+ width: 100%;
+ border: none;
+ outline: none;
+ }
+ button {
+ border: none;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ font-size: 1.25rem;
+ margin: 0.75rem 1rem;
+ color: white;
+ width: 7rem;
+ font-weight: 100 !important;
+ border-radius: 2.3125rem;
+ background: #402aa4;
+ }
+}
+
+.added-members {
+ display: flex;
+ flex-direction: row;
+ gap: 2rem;
+ flex-wrap: wrap;
+ width: 100%;
+ .member-card {
+ display: inline-flex;
+ padding: 0.4375rem 0.875rem 0.625rem 0.875rem;
+ justify-content: center;
+ align-items: center;
+ gap: 0.5625rem;
+ color: #8989ce;
+ border-radius: 0.75rem;
+ background: #26264e;
+ .member-avatar {
+ width: 40px;
+ height: 40px;
+ }
+ .btn-cross {
+ appearance: none;
+ border: none;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ background-color: transparent;
+ }
+ }
+}
+
+@media (min-width: $breakpoint-tablet) {
+ .main_aworkspace_container {
+ padding: 2rem 5rem;
+ .addworkspace-form-container {
+ padding: 2rem 5rem;
+ }
+ }
+}
diff --git a/src/features/WorkspaceAddMember/index.tsx b/src/features/WorkspaceAddMember/index.tsx
new file mode 100644
index 0000000..0126778
--- /dev/null
+++ b/src/features/WorkspaceAddMember/index.tsx
@@ -0,0 +1,171 @@
+import { getAllUser, getUser } from 'app/api/user';
+import { ChangeEvent, useContext, useEffect, useState } from 'react';
+import { useNavigate, useParams } from 'react-router-dom';
+import toast from 'react-hot-toast';
+import {
+ addOrgMembers,
+ getOrgMembers,
+} from 'app/api/organization';
+
+
+import './index.scss';
+import UserContext from 'app/context/user/userContext';
+import { AVATAR_URL } from 'app/constants/api';
+import { AVATAR_API } from 'envConstants';
+
+const WorkspaceAddMember = () => {
+ const navigate = useNavigate();
+ const token = localStorage.getItem('token');
+ const userContext = useContext(UserContext);
+
+ const [members, setMembers] = useState([]);
+ const [memberName, setMemberName] = useState(null);
+ const [orgMembers, setOrgMembers] = useState([]);
+ const [users, setUsers] = useState([]);
+ const { spaceName } = useParams();
+
+ const dataFetch = async () => {
+ try {
+ if (token && spaceName) {
+ const users_aray: string[] = [];
+
+ const allUser = await getAllUser(token);
+ const orgMembers = await getOrgMembers(token, spaceName);
+ allUser.data.users.forEach((user) => {
+ users_aray.push(user.username);
+ });
+ setOrgMembers(Object.keys(orgMembers.data.members));
+
+ setUsers(users_aray);
+ }
+ } catch (e) {}
+ };
+
+ useEffect(() => {
+ dataFetch();
+ }, []);
+
+ const addMembers = () => {
+ if (memberName) {
+ if (
+ users.includes(memberName) &&
+ memberName != userContext?.username &&
+ !members.includes(memberName) &&
+ !orgMembers.includes(memberName)
+ ) {
+ setMembers([...members, memberName]);
+ setMemberName(null);
+ }
+ }
+ };
+
+ const removeMembers = (member: string) => {
+ const indexToRemove = members.indexOf(member);
+
+ if (indexToRemove !== -1) {
+ const updatedMembers = [
+ ...members.slice(0, indexToRemove),
+ ...members.slice(indexToRemove + 1),
+ ];
+
+ setMembers(updatedMembers);
+ } else {
+ console.warn(`Member "${member}" not found in the members array.`);
+ }
+ };
+ const SubmitHandler = async (): Promise => {
+ if (token) {
+ const func = async (): Promise => {
+ if (members.length > 0 && spaceName) {
+ try {
+ const addMmebersRes = await addOrgMembers(
+ token,
+ spaceName,
+ members
+ );
+ } catch (e) {}
+ }
+ navigate(`/workspaceMembers/${spaceName}`);
+ };
+
+ toast.promise(func(), {
+ loading: 'On Progress',
+ success: Members added ,
+ error: Could not add ,
+ });
+ } else {
+ toast.error('Invalid inputs');
+ }
+ };
+
+ return (
+
+
+
+
+ ) => {
+ setMemberName(e.target.value);
+ }}
+ placeholder='Github ID of user'
+ />
+
+ {'+ Add'}
+
+
+
+
+ {members.map((member, index) => {
+ return (
+
+
{' '}
+
{member}
{' '}
+
{
+ removeMembers(member);
+ }}
+ className='btn-cross'
+ >
+
+
+ {' '}
+
+
+ );
+ })}
+
+
+
+ Done
+
+
+
+ );
+};
+
+export default WorkspaceAddMember;
diff --git a/src/features/home/components/leaderboard/index.tsx b/src/features/home/components/leaderboard/index.tsx
deleted file mode 100644
index ea38cd4..0000000
--- a/src/features/home/components/leaderboard/index.tsx
+++ /dev/null
@@ -1,87 +0,0 @@
-import React, { useState } from 'react';
-import gold from 'app/assets/images/gold.svg';
-import silver from 'app/assets/images/silver.svg';
-import bronze from 'app/assets/images/bronze.svg';
-import { mockData } from 'app/utils/data';
-import { sortJSON } from 'app/utils/sort';
-import leftNavButton from 'app/assets/images/left_navigation_button.svg';
-import rightNavButton from 'app/assets/images/right_navigation_button.svg';
-import mockdatatypes from 'app/models/mockDataTypes';
-import './index.scss';
-
-
-
-const LeaderBoard = () => {
- const [itemOffset, setItemOffset] = useState(0);
- const [pageNumber, setPageNumber] = useState(1);
- const newMockData = sortJSON(mockData);
-
- const [items] = useState(newMockData);
- const itemsPerPage = 4;
- const itemsLength = newMockData.length;
- const pageCount = Math.ceil(itemsLength / itemsPerPage);
-
- const handlePageClick = (_pageNumber: number) => {
- if (_pageNumber >= 1 && _pageNumber <= pageCount) {
- const newOffset = ((_pageNumber - 1) * itemsPerPage) % itemsLength;
- setItemOffset(newOffset);
- setPageNumber(_pageNumber);
- }
- };
-
- return (
-
-
-
-
- {items
- .slice(itemOffset, itemOffset + itemsPerPage)
- .map((e: mockdatatypes) => {
- if (e.Rank <= 3) {
- return (
-
-
-
-
-
{e.Name}
-
{e.PR}
-
- );
- } else {
- return (
-
-
{e.Rank}
-
{e.Name}
-
{e.PR}
-
- );
- }
- })}
-
-
-
handlePageClick(pageNumber - 1)}
- />
-
{pageNumber}
-
handlePageClick(pageNumber + 1)}
- />
-
-
-
-
- );
-};
-
-export default LeaderBoard;
diff --git a/src/features/home/components/projectCard/index.tsx b/src/features/home/components/projectCard/index.tsx
deleted file mode 100644
index fefe3cb..0000000
--- a/src/features/home/components/projectCard/index.tsx
+++ /dev/null
@@ -1,97 +0,0 @@
-import React, { useState } from 'react';
-import './index.scss';
-import axios from 'axios';
-import { getMembers, getProject } from 'app/api/project';
-import { useQuery } from 'react-query';
-import { GetProject } from 'app/api/project';
-import { ProjectMembers } from 'app/api/project';
-import { AVATAR_URL } from 'app/constants/api';
-import { AVATAR_API } from 'envConstants';
- interface Props{
- projectName: string,
- orgName: string,
- status: {
- archeive: boolean,
- bookmark: boolean,
- },
- githubData:{
- pulls: number,
- commits: number,
- issues: number
- } | null
-
-}
-
-
-
-
-
-const ProjectCard: React.FC = ({projectName,orgName ,status, githubData}) => {
- const token= localStorage.getItem('token')
-
- const [ProjectData,SetProjectData]= useState(null)
- const [projectMembers,setProjectMembers]= useState(null)
-
-const fetchProjectData= async()=>{
- if(token!=null){
- const project_data= await getProject(token,projectName, orgName);
- SetProjectData(project_data.data)
- return project_data.data
- }else{
- return null
- }
-}
-
-const fetchProjectMembers=async()=>{
- if(token!=null){
- const members= await getMembers(token,projectName,orgName);
- setProjectMembers(members.data)
- return members.data
-
- }
-}
-
-
-const {data:project_data}= useQuery(`${projectName}${orgName}`,fetchProjectData)
-const {data: project_members}= useQuery(`${projectName}${orgName}Members`, fetchProjectMembers);
-
-
-
- return (
-
-
{projectName}
-
- {project_data?project_data.description:<>>}
-
-
-
- Pull Requests
- {githubData?githubData.pulls:<>>}
-
-
- Commits
- {githubData?githubData.commits:<>>}
-
-
- Issues
- {githubData?githubData.issues:<>>}
-
-
-
-
- {/* A
- B
- C
- D */}
- {
- project_members&&Object.entries(project_members).slice(0,4).map(([key,value])=>{
- const url= AVATAR_URL+"/"+key+".png?apikey="+AVATAR_API
- return
- })
- }
-
-
- );
-};
-
-export default ProjectCard;
diff --git a/src/features/home/components/projectCardContainer/index.tsx b/src/features/home/components/projectCardContainer/index.tsx
deleted file mode 100644
index 4e98788..0000000
--- a/src/features/home/components/projectCardContainer/index.tsx
+++ /dev/null
@@ -1,54 +0,0 @@
-import React, { useState } from 'react';
-import ProjectCard from '../projectCard';
-import './index.scss';
-import { Projects } from 'app/api/organization';
-import { ProjectsGithubData } from 'app/api/githubData';
-
-const arr = [1, 2, 3, 4, 5];
-interface Props{
- weekly: boolean,
- orgName: string,
- orgProjects: Projects | null,
- monthlyOrgProjectsData:ProjectsGithubData | null,
- weeklyOrgProjectsData: ProjectsGithubData | null
-}
-
-const ProjectCardCont: React.FC = ({weekly,orgName ,orgProjects, monthlyOrgProjectsData, weeklyOrgProjectsData}) => {
-
-
- const [archeive,setArcheive]= useState(false);
- return (
- <>
-
- archeive?setArcheive(false):setArcheive(true)}>Archeive
-
-
-
-
- {orgProjects&&Object.entries(orgProjects).map(([key, value])=>{
- if(archeive&&value.archeive){
- if(weekly){
- let githubData=null
- if(weeklyOrgProjectsData){
- githubData= weeklyOrgProjectsData[key]
- }
-
- return
;
- }else{
- let githubData=null
- if(monthlyOrgProjectsData){
- githubData= monthlyOrgProjectsData[key]
- }
- return ;
-
- }
- }
- })}
-
-
-
- >
- );
-};
-
-export default ProjectCardCont;
diff --git a/src/features/home/index.tsx b/src/features/home/index.tsx
deleted file mode 100644
index ea2fac3..0000000
--- a/src/features/home/index.tsx
+++ /dev/null
@@ -1,143 +0,0 @@
-import React, { useEffect, useState } from 'react';
-import SearchBar from 'app/components/search';
-import TimeRangeSwitch from 'app/components/timeRangeSwitch';
-import ProjectCardCont from './components/projectCardContainer';
-import LeaderBoard from './components/leaderboard';
-import './index.scss';
-import { Navigate, useNavigate } from 'react-router-dom';
-import { getUser } from 'app/api/user';
-import toast from 'react-hot-toast';
-import { Query, useQuery } from 'react-query';
-import { getAllOrgs, getOrgProjects } from 'app/api/organization';
-import {
- getOrgGithubData,
- getOrgRank,
- getProjectGithubData,
-} from 'app/api/githubData';
-import { Projects } from 'app/api/organization';
-import { ProjectsGithubData } from 'app/api/githubData';
-import { Contributors } from 'app/api/githubData';
-import loader from '../../app/assets/gifs/loader.gif';
-
-const Home = () => {
- const navigate = useNavigate();
- const token = localStorage.getItem('token');
- const [userData, setUserData] = useState(null);
- const [weekly,setWeekly]= useState(true);
- const [orgProjects, setOrgProjects] = useState(null);
- const [monthlyOrgRank, setMonthlyOrgRank] = useState(
- null
- );
- const [weeklyOrgRank, setWeeklyOrgRank] = useState(null);
- const [monthlyOrgProjectsData, setMOnthyOrgProjectsData] =
- useState(null);
- const [weeklyOrgProjectsData, setWeeklyOrgProjectsData] =
- useState(null);
- const orgName = 'fordev';
- const checklogin = async () => {
- if (token != null) {
- try {
- const userData = await getUser(token);
-
- setUserData(userData.data.message);
- } catch (e) {
- toast.error('Session expired');
- navigate('/login');
- }
- } else {
- toast.error('Not authorized');
- navigate('/login');
- }
- };
-
- useEffect(() => {
- checklogin();
- }, []);
-
- const fetchOrgProjects = async () => {
- if (token) {
- const orgProjects = await getOrgProjects(token, orgName);
-
- setOrgProjects(orgProjects.data.projects);
-
-
- return {
- orgProjects: orgProjects.data.projects,
-
- };
- }
- };
-
-
- const fetchWeeklyData= async()=>{
- if(token){
- const weeklyOrgRank = await getOrgRank(token, orgName, true);
- const weeklyOrgProjectsData = await getOrgGithubData(
- token,
- orgName,
- false
- );
- setWeeklyOrgProjectsData(weeklyOrgProjectsData.data.projects);
- setWeeklyOrgRank(weeklyOrgRank.data.contributors);
- return {
- weeklyOrgRank: weeklyOrgRank.data.contributors,
- weeklyOrgProjectsData: weeklyOrgProjectsData.data.projects,
- }
-
- }
- }
-
- const fetchMonthlyData= async()=>{
- if(token){
- const monthlyOrgRank = await getOrgRank(token, orgName, true);
- const monthlyOrgProjectsData = await getOrgGithubData(
- token,
- orgName,
- true
- );
- setMonthlyOrgRank(monthlyOrgRank.data.contributors);
- setMOnthyOrgProjectsData(monthlyOrgProjectsData.data.projects);
- return{
- monthlyOrgProjectsData: monthlyOrgProjectsData.data.projects,
- monthlyOrgRank: monthlyOrgRank.data.contributors,
- }
- }
- }
-
- const { isLoading } = useQuery({
- queryFn: () => fetchOrgProjects(),
- queryKey: 'OrgProjects',
- });
-
- const {} = useQuery({
- queryFn: ()=> fetchWeeklyData(),
- queryKey: "weeklyData"
- })
-
- const {}= useQuery({
- queryFn: ()=>fetchMonthlyData(),
- queryKey: "monthlyData"
- })
-
-
- return (
- <>
-
-
-
-
- {isLoading?
-
-
:
-
-
- }
-
- >
- );
-};
-
-export default Home;
diff --git a/src/features/login/index.scss b/src/features/login/index.scss
index cc68ed4..b7666e5 100644
--- a/src/features/login/index.scss
+++ b/src/features/login/index.scss
@@ -1,68 +1,65 @@
.login_wrapper {
- display: flex;
- flex-direction: row;
- height: 100vh;
- position: absolute;
- z-index: -2;
- top: -6vh;
+ display: flex;
+ flex-direction: row;
+ height: 100vh;
+ position: absolute;
+ z-index: -2;
+ top: -6vh;
}
.hero_content {
- display: flex;
- align-items: center;
- justify-content: center;
- flex-direction: column;
- row-gap: 5vh;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ flex-direction: column;
+ row-gap: 5vh;
}
.hero_content h1 {
- color: rgba(255, 255, 255, 0.80);
- font-family: Montserrat;
- font-size: 59.815px;
- font-style: normal;
- font-weight: 700;
- line-height: normal;
- text-align: center;
- text-transform: uppercase;
+ color: rgba(255, 255, 255, 0.8);
+ font-family: Montserrat;
+ font-size: 59.815px;
+ font-style: normal;
+ font-weight: 700;
+ line-height: normal;
+ text-align: center;
+ text-transform: uppercase;
}
.hero_content h3 {
- color: rgba(223, 223, 223, 0.80);
- text-align: center;
- font-family: Poppins;
- font-size: 24px;
- font-style: normal;
- font-weight: 400;
- line-height: normal;
+ color: rgba(223, 223, 223, 0.8);
+ text-align: center;
+ font-family: Poppins;
+ font-size: 24px;
+ font-style: normal;
+ font-weight: 400;
+ line-height: normal;
}
.hero_content button {
- display: flex;
- width: 372px;
- height: 81px;
- padding: 20px 0px 20px 30px;
- align-items: center;
- gap: 20px;
- flex-shrink: 0;
- border-radius: 14px;
- border: 0.8px solid var(--Button, #402AA4);
- background: #402AA4;
- color: #ADADFF;
- font-family: Poppins;
- font-size: 20px;
- font-style: normal;
- font-weight: 500;
- line-height: normal;
+ display: flex;
+ width: 372px;
+ height: 81px;
+ padding: 20px 0px 20px 30px;
+ align-items: center;
+ gap: 20px;
+ flex-shrink: 0;
+ border-radius: 14px;
+ border: 0.8px solid var(--Button, #402aa4);
+ background: #402aa4;
+ color: #adadff;
+ font-family: Poppins;
+ font-size: 20px;
+ font-style: normal;
+ font-weight: 500;
+ line-height: normal;
}
-
.hero_image img {
- height: 105vh;
+ height: 105vh;
}
-
-.loader{
-
- height: 50px;
- width: 50px;
-}
\ No newline at end of file
+.loader {
+ height: 50px;
+ width: 50px;
+}
diff --git a/src/features/login/index.tsx b/src/features/login/index.tsx
index 417b83e..4d45ee5 100644
--- a/src/features/login/index.tsx
+++ b/src/features/login/index.tsx
@@ -1,11 +1,10 @@
-import { useEffect } from 'react';
+
import { CLIENT_ID } from '../../envConstants';
import { useNavigate, useSearchParams } from 'react-router-dom';
import { login } from 'app/api/login';
import { useQuery } from 'react-query';
-import {getUser } from 'app/api/user';
import toast from 'react-hot-toast';
-import loader from '../../app/assets/gifs/loader.gif'
+import loader from '../../app/assets/gifs/loader.gif';
import heroImg from 'app/assets/images/login2.png';
import github from 'app/assets/images/github.png';
@@ -14,82 +13,56 @@ import './index.scss';
const Login = () => {
const [searchParam] = useSearchParams();
const navigate = useNavigate();
-
-
- const token=localStorage.getItem('token')
- const checklogin=async()=>{
- if(token!=null){
- try{
- const userData= await getUser(token);
- navigate('/')
-
- }catch(e){
- localStorage.removeItem('token')
- navigate('/login')
- }
-
- }
- }
-
- useEffect(() => {
- checklogin();
- }, []);
-
-
const loginFunc = async () => {
if (searchParam.get('code') !== null) {
- const code: string = searchParam.get('code')!;
- const loginData = await login(code);
- const token = loginData.data.token;
- localStorage.setItem('token', token);
- toast.success("Login success")
- navigate('/');
+ try {
+ const code: string = searchParam.get('code')!;
+ const loginData = await login(code);
+ const token = loginData.data.token;
+ localStorage.setItem('token', token);
+ toast.success('Login success');
+ navigate('/');
+ } catch (e) {
+ toast.error('Some error occured');
+ navigate('/login');
+ }
}
};
- const { isError } = useQuery({
- queryFn: () => loginFunc(),
- queryKey: 'loginData',
+ const {} = useQuery('loginData', loginFunc, {
+ enabled: true,
+ staleTime: Infinity,
});
-
- if(isError){
- toast.error('Some error occured')
- navigate("/login")
-
- }
-
-
function loginWithGithub() {
window.location.assign(
'https://github.com/login/oauth/authorize?client_id=' + CLIENT_ID
);
}
- if(searchParam.get('code')!=null){
- return
+ if (searchParam.get('code') != null) {
+ return ;
}
return (
<>
-
-
-
+
+
+
+
+
+
ACTIVITY LEADERBOARD
+
+ Track your progress, healthy competition in organization others, and
+ unleash your GitHub potential"
+
+
+
+ Login Witth Github
+
+
-
-
ACTIVITY LEADERBOARD
-
- Track your progress, healthy competition in organization others, and
- unleash your GitHub potential"
-
-
-
- Login Witth Github
-
-
-
>
);
- }
-
+};
export default Login;
diff --git a/src/features/project-members /components/BackNavigation.tsx b/src/features/project-members /components/BackNavigation.tsx
new file mode 100644
index 0000000..3d0761f
--- /dev/null
+++ b/src/features/project-members /components/BackNavigation.tsx
@@ -0,0 +1,46 @@
+import React from 'react';
+import { useNavigate } from 'react-router-dom';
+
+interface Prop {
+ spaceName: string | undefined;
+}
+const BackNavigation: React.FC
= ({ spaceName }) => {
+ const navigate = useNavigate();
+ return (
+
+
navigate(`/workspace/${spaceName}`)}
+ >
+
+
+
+
+
+ {' '}
+ Back
+
+
+ Project {'->'} Manage
+
+
+ );
+};
+
+export default BackNavigation;
diff --git a/src/features/project-members /components/MemberCard.tsx b/src/features/project-members /components/MemberCard.tsx
new file mode 100644
index 0000000..88c24d7
--- /dev/null
+++ b/src/features/project-members /components/MemberCard.tsx
@@ -0,0 +1,128 @@
+
+import {
+ changeProjectMembersStatus,
+ removeProjectMembers,
+} from 'app/api/project';
+import UserContext from 'app/context/user/userContext';
+import {
+ ChangeEvent,
+ useContext,
+} from 'react';
+import toast from 'react-hot-toast';
+
+const MemberCard = ({
+ image,
+ name,
+ role,
+ orgMembers,
+ setProjectMembers,
+ projectMembers,
+ projectName,
+ spaceName,
+}: {
+ image: string;
+ name: string;
+ role: string;
+ spaceName: string;
+ projectName: string;
+ orgMembers: { [username: string]: string } | null;
+ projectMembers: { [username: string]: string } | null;
+ setProjectMembers: (projectMembers: { [username: string]: string }) => void;
+}) => {
+ const token = localStorage.getItem('token');
+ const userContext = useContext(UserContext);
+ const handleRemove = async () => {
+ if (token && spaceName && projectName && projectMembers) {
+ const func = async () => {
+ const res = await removeProjectMembers(token, projectName, spaceName, [
+ name,
+ ]);
+ delete projectMembers[name];
+ setProjectMembers(projectMembers);
+ };
+ toast.promise(func(), {
+ loading: 'Removing',
+ success: Successfully removed ,
+ error: Unable to remove ,
+ });
+ }
+ };
+
+ const HandleRoleChange = async (event: ChangeEvent) => {
+ const new_role = event.target.value;
+ if (
+ token &&
+ spaceName &&
+ orgMembers &&
+ projectMembers &&
+ new_role != role
+ ) {
+ const func = async () => {
+ const res = await changeProjectMembersStatus(
+ token,
+ projectName,
+ spaceName,
+ { [name]: new_role }
+ );
+ projectMembers[name] = new_role;
+ setProjectMembers(projectMembers);
+ };
+ toast.promise(func(), {
+ loading: 'Changing Role',
+ success: Role changed ,
+ error: Unable to change ,
+ });
+ }
+ };
+
+ return (
+
+
+
+
{name}
+
+
+
+ {orgMembers &&
+ projectMembers &&
+ userContext?.username &&
+ (orgMembers[userContext?.username.toString()] ==
+ ('admin' || 'manager') ||
+ userContext.username != name) &&
+ (orgMembers[userContext?.username.toString()] ==
+ ('admin' || 'manager') ||
+ projectMembers[userContext?.username.toString()] == 'admin') ? (
+
+ Admin
+ Member
+
+ ) : (
+
+ {role.charAt(0).toUpperCase() + role.slice(1).toLowerCase()}
+
+ )}
+
+ {orgMembers &&
+ projectMembers &&
+ userContext?.username &&
+ (orgMembers[userContext?.username.toString()] ==
+ ('admin' || 'manager') ||
+ userContext.username != name) &&
+ (orgMembers[userContext?.username.toString()] ==
+ ('admin' || 'manager') ||
+ projectMembers[userContext?.username.toString()] == 'admin') && (
+
+ Remove
+
+ )}
+
+
+ );
+};
+
+export default MemberCard;
diff --git a/src/features/project-members /components/Options.tsx b/src/features/project-members /components/Options.tsx
new file mode 100644
index 0000000..ecf9975
--- /dev/null
+++ b/src/features/project-members /components/Options.tsx
@@ -0,0 +1,44 @@
+import UserContext from 'app/context/user/userContext';
+import { useContext } from 'react';
+import { useNavigate } from 'react-router-dom';
+
+interface Props {
+ spaceName: string;
+ projectName: string;
+ projectMembers: { [username: string]: string } | null;
+ orgMembers: { [username: string]: string } | null;
+}
+
+const Options: React.FC = ({
+ spaceName,
+ projectName,
+ projectMembers,
+ orgMembers,
+}) => {
+ const navigate = useNavigate();
+ const userContext = useContext(UserContext);
+ return (
+
+ {orgMembers &&
+ projectMembers &&
+ spaceName &&
+ projectName &&
+ userContext?.username &&
+ (orgMembers[userContext?.username?.toString()] ==
+ ('admin' || 'manager') ||
+ projectMembers[userContext?.username.toString()] == 'admin') && (
+
+ navigate(`/projectAddMembers/${spaceName}/${projectName}`)
+ }
+ >
+ Add People
+
+ )}
+ {/* Filter */}
+
+ );
+};
+
+export default Options;
diff --git a/src/features/project-members /index.scss b/src/features/project-members /index.scss
new file mode 100644
index 0000000..5344489
--- /dev/null
+++ b/src/features/project-members /index.scss
@@ -0,0 +1,181 @@
+.members-page-container {
+ margin: 0rem 2rem;
+ margin-top: 2rem;
+
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ height: 100%;
+
+ .back-title-container {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ width: 100%;
+ }
+}
+
+.options {
+ width: 100%;
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+}
+
+.button {
+ outline: none;
+ border: none;
+ color: white;
+ font-size: 1.4rem;
+ padding: 1rem 2rem;
+ border-radius: 3rem;
+ background: #402aa4;
+}
+
+.add-people {
+ font-size: 1.2rem;
+ padding: 0.6rem 1.4rem;
+}
+
+.title {
+ color: rgba(237, 237, 237, 0.7);
+ font-family: Poppins;
+ font-size: 20px;
+ font-style: normal;
+ font-weight: 400;
+ line-height: normal;
+ letter-spacing: 1.6px;
+ text-transform: capitalize;
+ .arrow {
+ color: rgba(237, 237, 237, 0.7);
+ font-family: Poppins;
+ font-size: 20px;
+ font-style: normal;
+ font-weight: 400;
+ line-height: normal;
+ letter-spacing: -7.6px;
+ text-transform: capitalize;
+ }
+}
+
+.member-view {
+ margin-top: 1rem;
+ width: 60%;
+}
+
+.members-list {
+ background: transparent;
+ margin-top: 2rem;
+ display: flex;
+ flex-direction: column;
+ gap: 2rem;
+}
+.member-card {
+ border-radius: 20px;
+ border: 0.5px solid #406a80;
+ background: #191938;
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ justify-content: space-between;
+ gap: 1rem;
+ padding: 1rem 2rem;
+ position: relative;
+
+ .member-info {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ justify-content: space-between;
+ width: 40%;
+ gap: 0.5rem;
+ img {
+ width: 80px;
+ height: 80px;
+ border-radius: 50%;
+ }
+ .member-name {
+ font-size: 20px;
+ font-style: normal;
+ font-weight: 400;
+ line-height: normal;
+ letter-spacing: 1.6px;
+ text-transform: capitalize;
+ }
+ }
+
+ .member-actions {
+ width: 50%;
+ padding: 1rem;
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ justify-content: space-between;
+ .member-remove-btn {
+ right: 1rem;
+ text-decoration: none;
+ background: transparent;
+ border: none;
+ outline: none;
+ color: #db1b24;
+ font-size: 1.2rem;
+ }
+ .select-overlay {
+ padding-right: 1rem;
+ background: #402aa4;
+ cursor: pointer;
+ }
+ select {
+ border: none;
+ outline: none;
+ color: white;
+ font-size: 1.2rem;
+ padding: 0.6rem 1.4rem;
+ background: #402aa4;
+ cursor: pointer;
+ }
+ option {
+ padding: 1rem;
+ margin: 2rem;
+ }
+ }
+}
+
+@media only screen and (max-width: 1400px) {
+ .member-view {
+ width: 80%;
+ }
+}
+
+@media only screen and (max-width: 1100px) {
+ .member-card {
+ flex-direction: column;
+ .member-info {
+ width: 100%;
+ }
+ .member-actions {
+ width: 100%;
+ }
+ }
+}
+
+.role {
+ border: none;
+ outline: none;
+ color: white;
+ font-size: 1.2rem;
+ padding: 0.6rem 1rem;
+ background: #402aa4;
+ cursor: pointer;
+ width: 100%;
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+}
+
+@media only screen and (max-width: 600px) {
+ .member-view {
+ width: 90%;
+ }
+}
diff --git a/src/features/project-members /index.tsx b/src/features/project-members /index.tsx
new file mode 100644
index 0000000..5af9b6d
--- /dev/null
+++ b/src/features/project-members /index.tsx
@@ -0,0 +1,71 @@
+import { useParams } from 'react-router-dom';
+import BackNavigation from './components/BackNavigation';
+import MemberCard from './components/MemberCard';
+import Options from './components/Options';
+import './index.scss';
+import { useEffect, useState } from 'react';
+import { getOrgMembers } from 'app/api/organization';
+import { AVATAR_URL } from 'app/constants/api';
+import { AVATAR_API } from 'envConstants';
+import { getMembers } from 'app/api/project';
+const ProjectMembers = () => {
+ const { spaceName, projectName } = useParams();
+ const token = localStorage.getItem('token');
+ const [orgMembers, setOrgMembers] = useState<{
+ [username: string]: string;
+ } | null>(null);
+ const [projectMembers, setProjectMembers] = useState<{
+ [username: string]: string;
+ } | null>(null);
+
+ const dataFetch = async () => {
+ try {
+ if (spaceName && token && projectName) {
+ const projectMemRes = await getMembers(token, projectName, spaceName);
+ setProjectMembers(projectMemRes.data.members);
+ const orgMemRes = await getOrgMembers(token, spaceName);
+ setOrgMembers(orgMemRes.data.members);
+ }
+ } catch (e) {}
+ };
+ useEffect(() => {
+ dataFetch();
+ }, [setOrgMembers, setProjectMembers]);
+
+ return (
+
+
+ {spaceName && projectName && (
+
+
+
+ {orgMembers &&
+ projectMembers &&
+ Object.entries(projectMembers).map(([key, value]) => {
+ return (
+
+ );
+ })}
+
+
+ )}
+
+ );
+};
+
+export default ProjectMembers;
diff --git a/src/features/project/components/contributorCard/index.scss b/src/features/project/components/contributorCard/index.scss
index 129524a..74384fd 100644
--- a/src/features/project/components/contributorCard/index.scss
+++ b/src/features/project/components/contributorCard/index.scss
@@ -17,6 +17,8 @@
.contributor-card .contributor-image {
position: absolute;
margin-top: -50px;
+ height: 90px;
+ width: 90px;
}
.contributor-card .contributor-name {
diff --git a/src/features/project/components/contributorCard/index.tsx b/src/features/project/components/contributorCard/index.tsx
index 03c0232..0017431 100644
--- a/src/features/project/components/contributorCard/index.tsx
+++ b/src/features/project/components/contributorCard/index.tsx
@@ -1,26 +1,27 @@
-import React from 'react';
+
import './index.scss';
import contributorPropTypes from 'app/models/contributorPropTypes';
-import dumy_profile from 'app/assets/images/dumy_profile.svg';
-
+import { AVATAR_API } from 'envConstants';
+import { AVATAR_URL } from 'app/constants/api';
const ContributorCard = (props: contributorPropTypes) => {
- const { Name } = props;
+ const { Name, PR, Commits, Issues } = props;
+ const url = AVATAR_URL + '/' + Name + '.png?apikey=' + AVATAR_API;
return (
-
+
{Name}
Pull Requests
- 2
+ {PR}
Commits
- 2
+ {Commits}
Issues
- 2
+ {Issues}
diff --git a/src/features/project/components/contributors/index.tsx b/src/features/project/components/contributors/index.tsx
index 99a063e..17c8100 100644
--- a/src/features/project/components/contributors/index.tsx
+++ b/src/features/project/components/contributors/index.tsx
@@ -1,11 +1,10 @@
-import React, { useState } from 'react';
+import React, { useEffect, useState } from 'react';
import './index.scss';
import ContributorCard from '../contributorCard';
import next_contributor from 'app/assets/images/next_contributor.svg';
import previous_contributor from 'app/assets/images/previous_contributor.svg';
-import { mockData } from 'app/utils/data';
-import mockdatatypes from 'app/models/mockDataTypes';
import ReactSimplyCarousel from 'react-simply-carousel';
+import { Contributors as contri } from 'app/api/githubData';
const btn_style = {
alignSelf: 'center',
@@ -13,8 +12,62 @@ const btn_style = {
border: 'none',
};
-const Contributors = () => {
+interface Props {
+ weekly: boolean;
+ monthlyData: contri | null;
+ weeklyData: contri | null;
+}
+
+const Contributors: React.FC = ({ weekly, monthlyData, weeklyData }) => {
const [activeSlideIndex, setActiveSlideIndex] = useState(0);
+ const [monthlyDataArray, setMonthlyDataArray] = useState<
+ {
+ index: number;
+ username: string;
+ commits: number;
+ issues: number;
+ pulls: number;
+ }[]
+ >([]);
+ const [weeklyDataArray, setWeeklyDataArray] = useState<
+ {
+ index: number;
+ username: string;
+ commits: number;
+ issues: number;
+ pulls: number;
+ }[]
+ >([]);
+
+ const initialProcess = () => {
+ if (monthlyData) {
+ const contributorsArray = Object.entries(monthlyData).map(
+ ([username, data], index) => ({
+ index,
+ username,
+ ...data,
+ })
+ );
+ contributorsArray.sort((a, b) => b.pulls - a.pulls);
+ setMonthlyDataArray(contributorsArray);
+ }
+ if (weeklyData) {
+ const contributorsArray = Object.entries(weeklyData).map(
+ ([username, data], index) => ({
+ index,
+ username,
+ ...data,
+ })
+ );
+ contributorsArray.sort((a, b) => b.pulls - a.pulls);
+ setWeeklyDataArray(contributorsArray);
+ }
+ };
+
+ useEffect(() => {
+ initialProcess();
+ }, [weekly, monthlyData, weeklyData]);
+
return (
Contributors
@@ -62,24 +115,43 @@ const Contributors = () => {
speed={400}
easing='linear'
>
- {mockData.map((e: mockdatatypes) => {
- return (
-
-
-
- );
- })}
+ {weekly
+ ? weeklyDataArray.map((e) => {
+ return (
+
+
+
+ );
+ })
+ : monthlyDataArray.map((e) => {
+ return (
+
+
+
+ );
+ })}
diff --git a/src/features/project/index.tsx b/src/features/project/index.tsx
index ef0f46d..17d9e85 100644
--- a/src/features/project/index.tsx
+++ b/src/features/project/index.tsx
@@ -1,14 +1,60 @@
-import React from 'react';
+import { useEffect, useState } from 'react';
import Contributors from './components/contributors';
import ButtonBar from 'app/components/buttonBar';
import './index.scss';
+import { useParams } from 'react-router-dom';
+import { GetProject, getProject } from 'app/api/project';
+import {
+ Contributors as contri,
+ getProjectGithubData,
+} from 'app/api/githubData';
const ProjectPage = () => {
+ const { spaceName, projectName } = useParams();
+ const token = localStorage.getItem('token');
+ const [project, setProject] = useState(null);
+ const [weeklyData, setWeeklyData] = useState(null);
+ const [monthlyData, setMonthlyData] = useState(null);
+ const [weekly, setWeekly] = useState(true);
+ const fetchData = async () => {
+ if (spaceName && projectName && token) {
+ const project = await getProject(token, projectName, spaceName);
+ setProject(project.data);
+ const weeklyData = await getProjectGithubData(
+ token,
+ spaceName,
+ projectName,
+ false
+ );
+ const monthlyData = await getProjectGithubData(
+ token,
+ spaceName,
+ projectName,
+ true
+ );
+ setWeeklyData(weeklyData.data.contributors);
+ setMonthlyData(monthlyData.data.contributors);
+ }
+ };
+
+ useEffect(() => {
+ fetchData();
+ }, []);
+
return (
<>
-
-
+
+
>
);
diff --git a/src/features/workspace-members /components/BackNavigation.tsx b/src/features/workspace-members /components/BackNavigation.tsx
new file mode 100644
index 0000000..15a19bf
--- /dev/null
+++ b/src/features/workspace-members /components/BackNavigation.tsx
@@ -0,0 +1,39 @@
+import { useNavigate } from 'react-router-dom';
+
+const BackNavigation = () => {
+ const navigate = useNavigate();
+ return (
+
+
navigate('/')}>
+
+
+
+
+
+ {' '}
+ Back
+
+
+ Organization {'->'} Manage
+
+
+ );
+};
+
+export default BackNavigation;
diff --git a/src/features/workspace-members /components/MemberCard.tsx b/src/features/workspace-members /components/MemberCard.tsx
new file mode 100644
index 0000000..d8b0ac4
--- /dev/null
+++ b/src/features/workspace-members /components/MemberCard.tsx
@@ -0,0 +1,108 @@
+import { changeOrgMembersStatus, removeOrgMembers } from 'app/api/organization';
+import UserContext from 'app/context/user/userContext';
+import {
+ ChangeEvent,
+ useContext,
+} from 'react';
+import toast from 'react-hot-toast';
+
+const MemberCard = ({
+ image,
+ name,
+ role,
+ orgMembers,
+ setOrgMembers,
+ spaceName,
+}: {
+ image: string;
+ name: string;
+ role: string;
+ spaceName: string;
+ orgMembers: { [username: string]: string } | null;
+ setOrgMembers: (orgMembers: { [username: string]: string }) => void;
+}) => {
+ const token = localStorage.getItem('token');
+ const userContext = useContext(UserContext);
+ const handleRemove = async () => {
+ if (token && spaceName && orgMembers) {
+ const func = async () => {
+ const res = await removeOrgMembers(token, spaceName, [name]);
+ delete orgMembers[name];
+ setOrgMembers(orgMembers);
+ };
+ toast.promise(func(), {
+ loading: 'Removing',
+ success: Successfully removed ,
+ error: Unable to remove ,
+ });
+ }
+ };
+
+ const HandleRoleChange = async (event: ChangeEvent) => {
+ const new_role = event.target.value;
+ if (token && spaceName && orgMembers && new_role != role) {
+ const func = async () => {
+ const res = await changeOrgMembersStatus(token, spaceName, {
+ [name]: new_role,
+ });
+ orgMembers[name] = new_role;
+ setOrgMembers(orgMembers);
+
+ toast.promise(func(), {
+ loading: 'Changing Role',
+ success: Role changed ,
+ error: Unable to change ,
+ });
+ orgMembers[name] = new_role;
+ setOrgMembers(orgMembers);
+ };
+ toast.promise(func(), {
+ loading: 'Changing Role',
+ success: Role changed ,
+ error: Unable to change ,
+ });
+ }
+ };
+
+ return (
+
+
+
+
{name}
+
+
+
+ {orgMembers &&
+ userContext?.username &&
+ userContext.username != name &&
+ orgMembers[userContext?.username.toString()] == 'admin' ? (
+
+ Admin
+ Manager
+ Member
+
+ ) : (
+
+ {role.charAt(0).toUpperCase() + role.slice(1).toLowerCase()}
+
+ )}
+
+ {orgMembers &&
+ userContext?.username &&
+ userContext.username != name &&
+ orgMembers[userContext?.username.toString()] == 'admin' && (
+
+ Remove
+
+ )}
+
+
+ );
+};
+
+export default MemberCard;
diff --git a/src/features/workspace-members /components/Options.tsx b/src/features/workspace-members /components/Options.tsx
new file mode 100644
index 0000000..da8fed5
--- /dev/null
+++ b/src/features/workspace-members /components/Options.tsx
@@ -0,0 +1,22 @@
+import { useNavigate } from 'react-router-dom';
+
+interface Props {
+ spaceName: string;
+}
+
+const Options: React.FC = ({ spaceName }) => {
+ const navigate = useNavigate();
+ return (
+
+ navigate(`/workspaceAddMembers/${spaceName}`)}
+ >
+ Add People
+
+ {/* Filter */}
+
+ );
+};
+
+export default Options;
diff --git a/src/features/workspace-members /index.scss b/src/features/workspace-members /index.scss
new file mode 100644
index 0000000..5344489
--- /dev/null
+++ b/src/features/workspace-members /index.scss
@@ -0,0 +1,181 @@
+.members-page-container {
+ margin: 0rem 2rem;
+ margin-top: 2rem;
+
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ height: 100%;
+
+ .back-title-container {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ width: 100%;
+ }
+}
+
+.options {
+ width: 100%;
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+}
+
+.button {
+ outline: none;
+ border: none;
+ color: white;
+ font-size: 1.4rem;
+ padding: 1rem 2rem;
+ border-radius: 3rem;
+ background: #402aa4;
+}
+
+.add-people {
+ font-size: 1.2rem;
+ padding: 0.6rem 1.4rem;
+}
+
+.title {
+ color: rgba(237, 237, 237, 0.7);
+ font-family: Poppins;
+ font-size: 20px;
+ font-style: normal;
+ font-weight: 400;
+ line-height: normal;
+ letter-spacing: 1.6px;
+ text-transform: capitalize;
+ .arrow {
+ color: rgba(237, 237, 237, 0.7);
+ font-family: Poppins;
+ font-size: 20px;
+ font-style: normal;
+ font-weight: 400;
+ line-height: normal;
+ letter-spacing: -7.6px;
+ text-transform: capitalize;
+ }
+}
+
+.member-view {
+ margin-top: 1rem;
+ width: 60%;
+}
+
+.members-list {
+ background: transparent;
+ margin-top: 2rem;
+ display: flex;
+ flex-direction: column;
+ gap: 2rem;
+}
+.member-card {
+ border-radius: 20px;
+ border: 0.5px solid #406a80;
+ background: #191938;
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ justify-content: space-between;
+ gap: 1rem;
+ padding: 1rem 2rem;
+ position: relative;
+
+ .member-info {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ justify-content: space-between;
+ width: 40%;
+ gap: 0.5rem;
+ img {
+ width: 80px;
+ height: 80px;
+ border-radius: 50%;
+ }
+ .member-name {
+ font-size: 20px;
+ font-style: normal;
+ font-weight: 400;
+ line-height: normal;
+ letter-spacing: 1.6px;
+ text-transform: capitalize;
+ }
+ }
+
+ .member-actions {
+ width: 50%;
+ padding: 1rem;
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ justify-content: space-between;
+ .member-remove-btn {
+ right: 1rem;
+ text-decoration: none;
+ background: transparent;
+ border: none;
+ outline: none;
+ color: #db1b24;
+ font-size: 1.2rem;
+ }
+ .select-overlay {
+ padding-right: 1rem;
+ background: #402aa4;
+ cursor: pointer;
+ }
+ select {
+ border: none;
+ outline: none;
+ color: white;
+ font-size: 1.2rem;
+ padding: 0.6rem 1.4rem;
+ background: #402aa4;
+ cursor: pointer;
+ }
+ option {
+ padding: 1rem;
+ margin: 2rem;
+ }
+ }
+}
+
+@media only screen and (max-width: 1400px) {
+ .member-view {
+ width: 80%;
+ }
+}
+
+@media only screen and (max-width: 1100px) {
+ .member-card {
+ flex-direction: column;
+ .member-info {
+ width: 100%;
+ }
+ .member-actions {
+ width: 100%;
+ }
+ }
+}
+
+.role {
+ border: none;
+ outline: none;
+ color: white;
+ font-size: 1.2rem;
+ padding: 0.6rem 1rem;
+ background: #402aa4;
+ cursor: pointer;
+ width: 100%;
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+}
+
+@media only screen and (max-width: 600px) {
+ .member-view {
+ width: 90%;
+ }
+}
diff --git a/src/features/workspace-members /index.tsx b/src/features/workspace-members /index.tsx
new file mode 100644
index 0000000..1c83020
--- /dev/null
+++ b/src/features/workspace-members /index.tsx
@@ -0,0 +1,58 @@
+import { useParams } from 'react-router-dom';
+import BackNavigation from './components/BackNavigation';
+import MemberCard from './components/MemberCard';
+import Options from './components/Options';
+import './index.scss';
+import { useEffect, useState } from 'react';
+import { getOrgMembers } from 'app/api/organization';
+import { AVATAR_URL } from 'app/constants/api';
+import { AVATAR_API } from 'envConstants';
+const WorkspaceMembers = () => {
+ const { spaceName } = useParams();
+ const token = localStorage.getItem('token');
+ const [orgMembers, setOrgMembers] = useState<{
+ [username: string]: string;
+ } | null>(null);
+
+ const dataFetch = async () => {
+ try {
+ if (spaceName && token) {
+ const memRes = await getOrgMembers(token, spaceName);
+
+ setOrgMembers(memRes.data.members);
+ }
+ } catch (e) {}
+ };
+ useEffect(() => {
+ dataFetch();
+ }, [setOrgMembers]);
+
+ return (
+
+
+ {spaceName && (
+
+
+
+ {orgMembers &&
+ Object.entries(orgMembers).map(([key, value]) => {
+ return (
+
+ );
+ })}
+
+
+ )}
+
+ );
+};
+
+export default WorkspaceMembers;
diff --git a/src/features/workspace-view/index.scss b/src/features/workspace-view/index.scss
index a665adf..75bd315 100644
--- a/src/features/workspace-view/index.scss
+++ b/src/features/workspace-view/index.scss
@@ -18,6 +18,7 @@
.workspaceview-card-container {
background: linear-gradient(110.51deg, #141432 0.9%, #2a2a4b 101.51%);
+ padding: 3rem 1rem;
width: 70%;
margin-left: auto;
margin-right: auto;
@@ -25,16 +26,21 @@
border-radius: 20px;
display: grid;
grid-template-columns: repeat(2, 1fr);
- row-gap: 2em;
- column-gap: 2em;
- padding: 1em;
+ row-gap: 3em;
+ column-gap: 3em;
+
justify-items: center;
align-items: center;
}
-@media only screen and (max-width: 860px) {
+@media only screen and (max-width: 1100px) {
.workspaceview-card-container {
grid-template-columns: 1fr;
width: 90%;
}
}
+@media only screen and (max-width: 1600px) {
+ .workspaceview-card-container {
+ width: 90%;
+ }
+}
diff --git a/src/features/workspace-view/index.tsx b/src/features/workspace-view/index.tsx
index ceed34c..3a2af2b 100644
--- a/src/features/workspace-view/index.tsx
+++ b/src/features/workspace-view/index.tsx
@@ -1,28 +1,97 @@
import SearchBar from 'app/components/search';
-import React from 'react';
+import { useContext, useEffect, useState } from 'react';
import './index.scss';
import WorkspaceCard from './workspace-card';
-import { workSpaceData } from 'app/utils/workspaceData';
+import UserContext from 'app/context/user/userContext';
+import { UserOrgDetails, getUserOrgs } from 'app/api/user';
+import loader from '../../app/assets/gifs/loader.gif';
+import { useNavigate } from 'react-router-dom';
+import { useSelector } from 'react-redux';
+import FirstVisit from 'app/components/firstVisit';
+import toast from 'react-hot-toast';
const WorkspaceView = () => {
+ const userContext = useContext(UserContext);
+ const [isLoad, setIsLoad] = useState(false);
+ const [archeives, setArcheives] = useState(false);
+ const token = localStorage.getItem('token');
+ const navigate = useNavigate();
+ const fetchData = async () => {
+ if (token && userContext?.username) {
+ setIsLoad(true);
+ try {
+ const userOrgs = await getUserOrgs(
+ token,
+ userContext?.username.toString()
+ );
+ userContext?.setUserOrgs(userOrgs.data);
+
+ } catch (e) {}
+
+ setIsLoad(false);
+ }
+ };
+ const searchValue = useSelector((state: any) => state.searchKeyword.value);
+
+ useEffect(() => {
+ fetchData();
+ }, [
+ userContext?.setUsername,
+ userContext?.username,
+ navigate,
+ userContext?.setUserOrgs,
+ searchValue,
+ ]);
+
+ const LogoutHandler = async () => {
+ try {
+ localStorage.removeItem('token');
+ toast.success('Logout successful!');
+ navigate('/login');
+ } catch (e) {}
+ };
return (
+
- Create a workspace
+
+ setArcheives(!archeives)}
+ style={archeives ? { background: '#141432' } : {}}
+ >
+ Archeives
+
+
+ navigate('/addWorkspace')}>
+ Create a workspace
+
+ Logout
+
- {workSpaceData.map((workspace) => {
- return (
-
- );
- })}
+ {isLoad ? (
+
+ ) : (
+ userContext?.userOrgs &&
+ Object.entries(userContext.userOrgs.userOrgs)
+ .filter(([key, value]) => {
+ if (key.toLowerCase().includes(searchValue.toLowerCase()))
+ return [key, value];
+ })
+ .map(([orgName, details]) => {
+ return (
+
+ );
+ })
+ )}
);
diff --git a/src/features/workspace-view/workspace-card/index.scss b/src/features/workspace-view/workspace-card/index.scss
index c826b6c..d65e41f 100644
--- a/src/features/workspace-view/workspace-card/index.scss
+++ b/src/features/workspace-view/workspace-card/index.scss
@@ -3,9 +3,10 @@
position: relative;
border: 2px solid #402aa4;
border-radius: 20px;
- width: 454px;
- height: 256px;
- padding: 12px 32px;
+ max-width: 554px;
+ width: 100%;
+ height: 306px;
+ padding: 2rem 3rem;
}
.workspace-logo img {
background: #402aa4;
@@ -18,17 +19,27 @@
.workspace-card-utils {
display: flex;
}
+.members-view-container {
+ display: flex;
+ flex-direction: row;
+ gap: 2rem;
+ align-items: center;
+}
.workspace-members {
- padding: 10px 17px;
+ margin-left: 2rem;
+ display: flex;
+ flex-direction: column;
+ gap: 1rem;
+ width: 100%;
+}
+.workspace-description {
+ height: 100%;
+ font-size: 1.1rem;
+ height: 100%;
}
.workspace-title {
- font-family: 'Poppins';
- font-style: normal;
- font-weight: 600;
- font-size: 20px;
- line-height: 30px;
- /* identical to box height */
-
+ font-weight: 900 !important;
+ font-size: 1.6rem;
color: #ededed;
}
.workspace-card-body {
@@ -36,10 +47,11 @@
.workspace-details-btn {
display: flex;
justify-content: end;
- height: 30px;
+ height: 50px;
}
.workspace-details-btn img {
- border-radius: 20px;
+ border-radius: 2rem;
+ font-size: 1rem;
}
.hide {
@@ -61,9 +73,10 @@
position: absolute;
width: 111px;
- height: 110px;
+ height: fit-content !important;
background: rgba(26, 26, 57, 0.5);
+ background: black;
border: 1px solid #402aa4;
backdrop-filter: blur(2px);
/* Note: backdrop-filter has minimal browser support */
@@ -84,3 +97,7 @@
opacity: 1;
}
}
+
+.image-stack {
+ margin-left: 1rem;
+}
diff --git a/src/features/workspace-view/workspace-card/index.tsx b/src/features/workspace-view/workspace-card/index.tsx
index 12ce276..c95457d 100644
--- a/src/features/workspace-view/workspace-card/index.tsx
+++ b/src/features/workspace-view/workspace-card/index.tsx
@@ -1,45 +1,271 @@
-import React, { useState } from 'react';
+import React, { useContext, useEffect, useState } from 'react';
import './index.scss';
import rightNavbtn from '../../../app/assets/images/right_navigation_button.svg';
-import workspaceCardProps from 'app/models/workSpaceCardTypes';
-const WorkspaceCard = (props: workspaceCardProps) => {
- const { imgURL, title, description } = props;
+import { deleteOrg, getOrg, getOrgMembers } from 'app/api/organization';
+import { deleteFile, getIcon, getIconName } from 'app/api/file';
+import UserContext from 'app/context/user/userContext';
+import toast from 'react-hot-toast';
+import { FaBookmark } from "react-icons/fa";
+
+
+import {
+ UserOrgDetails,
+ setOrgArcheiveStatus,
+ setOrgBookmarkStatus,
+} from 'app/api/user';
+import { useNavigate } from 'react-router-dom';
+import { AVATAR_API } from 'envConstants';
+import { AVATAR_URL } from 'app/constants/api';
+type workspaceCardProps = {
+ workspaceName: string;
+ role: string;
+ archeive: boolean;
+ bookmark: boolean;
+ archeives: boolean;
+};
+
+interface members {
+ [username: string]: string;
+}
+const WorkspaceCard = (props: workspaceCardProps) => {
+ const { workspaceName, role, archeive, bookmark, archeives } = props;
+ const [description, setDescription] = useState(null);
const [showPopUp, setShowPopUp] = useState(false);
+ const token = localStorage.getItem('token');
+ const [imageSrc, setImageSrc] = useState(null);
+ const [members, setMembers] = useState(null);
+ const [membersArray, setMembersArray] = useState<{ username: string }[]>([]);
+
+ const userContext = useContext(UserContext);
+ const navigate = useNavigate();
+ const workSpaceData = async () => {
+ if (token && workspaceName && !workspaceName.endsWith('-userspace')) {
+ try {
+ const workspace_data = await getOrg(token, workspaceName);
+
+ setDescription(workspace_data.data.description);
+ } catch (e) {}
+ try {
+ const image_data = await getIcon(token, workspaceName);
+ const objectUrl = URL.createObjectURL(image_data.data);
+ setImageSrc(objectUrl);
+ } catch (e) {}
+ try {
+ const members_data = await getOrgMembers(token, workspaceName);
+ setMembers(members_data.data.members);
+ const membersArray = Object.entries(members_data.data.members).map(
+ ([username]) => ({ username })
+ );
+ setMembersArray(membersArray);
+ } catch (e) {}
+
+ try {
+ const iconRes = await getIconName(token, workspaceName);
+ const fileName = iconRes.data.message;
+ const deleteRes = await deleteFile(token, fileName);
+ } catch (e) {}
+ }
+ };
+
+ const HandleDelete = async () => {
+ if (!workspaceName.endsWith('-userspace') && token) {
+ const func = async () => {
+ const res = await deleteOrg(token, workspaceName);
+ const orgs = userContext?.userOrgs;
+
+ if (orgs?.userOrgs.hasOwnProperty(workspaceName)) {
+ const obj: UserOrgDetails = orgs.userOrgs;
+ delete obj[workspaceName];
+ userContext?.setUserOrgs({
+ userOrgs: obj,
+ });
+ }
+ };
+ toast.promise(func(), {
+ loading: 'Deleting',
+ success: Successfully Deleted ,
+ error: Error while deleting ,
+ });
+ }
+ };
+
+ const HandlePin = async () => {
+ if (!workspaceName.endsWith('-userspace') && token) {
+ const initBmk = bookmark;
+ const func = async () => {
+ let status: { [key: string]: boolean } = {
+ [workspaceName]: !bookmark,
+ };
+
+ const res = await setOrgBookmarkStatus(token, status);
+ const orgs = userContext?.userOrgs;
+ if (orgs?.userOrgs.hasOwnProperty(workspaceName)) {
+ orgs.userOrgs[workspaceName].bookmark = (!bookmark).toString();
+ userContext?.setUserOrgs(orgs);
+ }
+ };
+ if (initBmk) {
+ toast.promise(func(), {
+ loading: 'Unpinning',
+ success: Successfully unpinned ,
+ error: Error while unpinning ,
+ });
+ } else {
+ toast.promise(func(), {
+ loading: 'Pinning',
+ success: Successfully pinned ,
+ error: Error while pinning ,
+ });
+ }
+ }
+ };
+
+ const HandleArchive = async () => {
+ if (!workspaceName.endsWith('-userspace') && token) {
+ const initArc = archeive;
+
+ const func = async () => {
+ const status: { [key: string]: boolean } = {
+ [workspaceName]: !archeive,
+ };
+
+ const res = await setOrgArcheiveStatus(token, status);
+
+ const orgs = userContext?.userOrgs;
+ if (orgs?.userOrgs.hasOwnProperty(workspaceName)) {
+ orgs.userOrgs[workspaceName].archeive = (!archeive).toString();
+ userContext?.setUserOrgs(orgs);
+ }
+ };
+ if (!initArc) {
+ toast.promise(func(), {
+ loading: 'Archiving',
+ success: Successfully archived ,
+ error: Error ,
+ });
+ } else {
+ toast.promise(func(), {
+ loading: 'Unarchiving',
+ success: Successfully unarchived ,
+ error: Error ,
+ });
+ }
+ }
+ };
+ useEffect(() => {
+ workSpaceData();
+ }, [userContext?.setUserOrgs, userContext?.setUsername]);
+
return (
-
-
-
setShowPopUp(showPopUp ? false : true)}
- >
-
-
-
-
Pin
-
archive
-
delete
-
-
-
-
-
-
-
{title}
-
img
+ <>
+ {archeive == archeives && (
+
+
+
+ {bookmark&&
}
+
setShowPopUp(showPopUp ? false : true)}
+ >
+ {!workspaceName.endsWith('-userspace') && (
+
+ )}
+
+
+
+ {bookmark ? 'UnPin' : 'Pin'}
+
+
+ {archeive ? 'Unarchive' : 'archive'}
+
+ {members &&
+ userContext?.username &&
+ members[userContext?.username.toString()] === 'admin' && (
+
+ delete
+
+ )}
+
+ {members &&
+ userContext?.username &&
+ members[userContext?.username.toString()] === 'admin' && (
+
navigate(`/editWorkspace/${workspaceName}`)}
+ >
+ edit
+
+ )}
+
+
+
+
+
+
+
+ {workspaceName.endsWith('-userspace')
+ ? "USER's WORKSPACE"
+ : workspaceName}
+
+
navigate(`/workspaceMembers/${workspaceName}`)}>
+
+
+ {membersArray.length > 0 ? (
+ membersArray.slice(0, 4).map((obj) => {
+ const url =
+ AVATAR_URL +
+ '/' +
+ obj.username +
+ '.png?apikey=' +
+ AVATAR_API;
+ return (
+
+ );
+ })
+ ) : (
+ <>
+
+ >
+ )}
+
+
+
+
{members ? Object.keys(members).length : 0} members
+
+
+
+
+ {workspaceName.endsWith('-userspace') &&
+ "User's private workspace"}
+ {description ? description.substring(0, 120) + '...' : <>>}
+
+
+
navigate(`/workspace/${workspaceName}`)}
+ alt=''
+ />
+
-
- {description.substring(0, 120) + '...'}
-
-
-
-
-
-
+ )}
+ >
);
};
diff --git a/src/features/home/components/leaderboard/index.scss b/src/features/workspace/components/leaderboard/index.scss
similarity index 100%
rename from src/features/home/components/leaderboard/index.scss
rename to src/features/workspace/components/leaderboard/index.scss
diff --git a/src/features/workspace/components/leaderboard/index.tsx b/src/features/workspace/components/leaderboard/index.tsx
new file mode 100644
index 0000000..76de64c
--- /dev/null
+++ b/src/features/workspace/components/leaderboard/index.tsx
@@ -0,0 +1,132 @@
+import React, { useEffect, useState } from 'react';
+import gold from 'app/assets/images/gold.svg';
+import silver from 'app/assets/images/silver.svg';
+import bronze from 'app/assets/images/bronze.svg';
+import leftNavButton from 'app/assets/images/left_navigation_button.svg';
+import rightNavButton from 'app/assets/images/right_navigation_button.svg';
+import './index.scss';
+import { Contributors } from 'app/api/githubData';
+
+interface Prop {
+ weekly: boolean;
+ weeklyOrgRank: Contributors | null;
+ monthlyOrgRank: Contributors | null;
+}
+
+const LeaderBoard: React.FC
= ({
+ weekly,
+ weeklyOrgRank,
+ monthlyOrgRank,
+}) => {
+ const [itemOffset, setItemOffset] = useState(0);
+ const [pageNumber, setPageNumber] = useState(1);
+
+ // const newMockData = sortJSON(mockData);
+
+ const [items, setItems] = useState<
+ {
+ index: number;
+ name: string;
+ issues: number;
+ commits: number;
+ pulls: number;
+ }[]
+ >([]);
+
+ const itemsPerPage = 4;
+
+ const [itemsLength, setItemsLength] = useState(0);
+ const [pageCount, setPageCount] = useState(0);
+ // const pageCount = Math.ceil(itemsLength / itemsPerPage);
+
+ const handlePageClick = (_pageNumber: number) => {
+ if (_pageNumber >= 1 && _pageNumber <= pageCount) {
+ const newOffset = ((_pageNumber - 1) * itemsPerPage) % itemsLength;
+ setItemOffset(newOffset);
+ setPageNumber(_pageNumber);
+ }
+ };
+
+ useEffect(() => {
+ if (weekly && weeklyOrgRank) {
+ const arrayOfContributors = Object.entries(weeklyOrgRank).map(
+ ([name, data], index) => ({ index, name, ...data })
+ );
+
+ setItems(arrayOfContributors);
+ setItemsLength(Object.keys(weeklyOrgRank).length);
+ setPageCount(Math.ceil(Object.keys(weeklyOrgRank).length / itemsPerPage));
+ } else if (!weekly && monthlyOrgRank) {
+ const arrayOfContributors = Object.entries(monthlyOrgRank).map(
+ ([name, data], index) => ({ index, name, ...data })
+ );
+
+ setItems(arrayOfContributors);
+ setItemsLength(Object.keys(monthlyOrgRank).length);
+ setPageCount(
+ Math.ceil(Object.keys(monthlyOrgRank).length / itemsPerPage)
+ );
+ }
+ }, [weekly, weeklyOrgRank, monthlyOrgRank]);
+
+ return (
+
+
+
+
+ {items &&
+ items.slice(itemOffset, itemOffset + itemsPerPage).map((e) => {
+ if (e.index + 1 <= 3) {
+ return (
+
+
+
+
+
{e.name}
+
{e.pulls}
+
+ );
+ } else {
+ return (
+
+
{e.index + 1}
+
{e.name}
+
{e.pulls}
+
+ );
+ }
+ })}
+
+
+
handlePageClick(pageNumber - 1)}
+ />
+
{pageNumber}
+
handlePageClick(pageNumber + 1)}
+ />
+
+
+
+
+ );
+};
+
+export default LeaderBoard;
diff --git a/src/features/home/components/projectCard/index.scss b/src/features/workspace/components/projectCard/index.scss
similarity index 52%
rename from src/features/home/components/projectCard/index.scss
rename to src/features/workspace/components/projectCard/index.scss
index a50a842..eb9be42 100644
--- a/src/features/home/components/projectCard/index.scss
+++ b/src/features/workspace/components/projectCard/index.scss
@@ -1,13 +1,19 @@
.projectcard {
+ position: relative;
border-radius: 1em;
background-color: var(--home-page-card-bg);
+ max-width: 554px;
+ width: 90%;
+ height: 100%;
+ min-height: 257px;
padding: 1em;
-}
-.projectcard:hover {
- cursor: pointer;
}
+// .projectcard:hover {
+// cursor: pointer;
+// }
+
.projectcard h1 {
color: #ededed;
margin-bottom: 0.2em;
@@ -18,6 +24,10 @@
margin-bottom: 0.6em;
}
+.invisible-height {
+ height: 30px;
+}
+
.projectcard-status {
display: flex;
justify-content: space-between;
@@ -81,3 +91,78 @@
.projectcard-contributor li:nth-child(4n-3) {
background: #e8aa0a;
}
+
+.image-stack {
+ margin-left: 1rem;
+}
+
+.project-image {
+ display: inline-block;
+ margin-left: -10px;
+ width: 28px;
+ height: 28px;
+ border: 1px solid white;
+ border-radius: 50%;
+}
+
+.hide {
+ display: none;
+}
+.workspace-popup {
+ position: absolute;
+ right: 50px;
+ top: 50px;
+ box-sizing: border-box;
+
+ /* Auto layout */
+
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+ padding: 18px 33px;
+ gap: 10px;
+
+ position: absolute;
+ width: 111px;
+ height: 110px;
+
+ background: rgba(26, 26, 57, 0.5);
+ border: 1px solid #402aa4;
+ backdrop-filter: blur(2px);
+ /* Note: backdrop-filter has minimal browser support */
+
+ border-radius: 12px;
+}
+
+.workspace-popup-btn {
+ position: absolute;
+ right: 20px;
+ top: 20px;
+}
+.workspace-popup-btn img {
+ color: white;
+}
+@keyframes fadeIn {
+ from {
+ opacity: 0;
+ }
+ to {
+ opacity: 1;
+ }
+}
+
+.workspace-details-btn {
+ display: flex;
+ justify-content: end;
+ height: 50px;
+}
+.workspace-details-btn img {
+ border-radius: 2rem;
+ font-size: 1rem;
+}
+
+.pinDiv{
+ height: 12px;
+
+}
\ No newline at end of file
diff --git a/src/features/workspace/components/projectCard/index.tsx b/src/features/workspace/components/projectCard/index.tsx
new file mode 100644
index 0000000..8d28722
--- /dev/null
+++ b/src/features/workspace/components/projectCard/index.tsx
@@ -0,0 +1,220 @@
+import React, { useContext, useEffect, useState } from 'react';
+import './index.scss';
+import { deleteProject, getMembers, getProject } from 'app/api/project';
+import { FaBookmark } from "react-icons/fa";
+import { GetProject } from 'app/api/project';
+import { AVATAR_URL } from 'app/constants/api';
+import { AVATAR_API } from 'envConstants';
+import UserContext from 'app/context/user/userContext';
+import { setArcheiveStatus, setBookmarkStatus } from 'app/api/organization';
+import toast from 'react-hot-toast';
+import { useNavigate } from 'react-router-dom';
+import rightNavbtn from '../../../../app/assets/images/right_navigation_button.svg';
+interface Props {
+ projectName: string;
+ orgName: string;
+ status: {
+ archeive: boolean;
+ bookmark: boolean;
+ };
+ githubData: {
+ pulls: number;
+ commits: number;
+ issues: number;
+ } | null;
+}
+
+const ProjectCard: React.FC = ({
+ projectName,
+ orgName,
+
+ status,
+ githubData,
+}) => {
+ const token = localStorage.getItem('token');
+ const [showPopUp, setShowPopUp] = useState(false);
+ const userContext = useContext(UserContext);
+ const navigate = useNavigate();
+ const [pin, setPin] = useState(status.bookmark);
+ const [archive, setArchive] = useState(status.archeive);
+ const [project, setProject] = useState(null);
+ const [projectMembers, setProjectMembers] = useState<
+ { key: string; value: string }[]
+ >([]);
+
+ const fetchProjectData = async () => {
+ if (token != null) {
+ const project_data = await getProject(token, projectName, orgName);
+ setProject(project_data.data);
+ }
+ };
+
+ const fetchProjectMembers = async () => {
+ if (token != null) {
+ const members = await getMembers(token, projectName, orgName);
+ const myArray = Object.entries(members.data.members).map(
+ ([key, value]) => ({ key, value })
+ );
+
+ setProjectMembers(myArray);
+ }
+ };
+
+ const PinHandler = async () => {
+ if (token && orgName) {
+ let initial = pin;
+ const func = async () => {
+ const res = await setBookmarkStatus(token, orgName, {
+ [projectName]: !pin,
+ });
+ setPin(!pin);
+ };
+
+ if (initial) {
+ toast.promise(func(), {
+ loading: 'On progress',
+ success: Project Unpinned ,
+ error: unable to unpin ,
+ });
+ } else {
+ toast.promise(func(), {
+ loading: 'On progress',
+ success: Project pinned ,
+ error: unable to pin ,
+ });
+ }
+ }
+ };
+
+ const ArchiveHandler = async () => {
+ if (token && orgName) {
+ let initial = archive;
+ const func = async () => {
+ const res = await setArcheiveStatus(token, orgName, {
+ [projectName]: !archive,
+ });
+ setArchive(!archive);
+ };
+
+ if (initial) {
+ toast.promise(func(), {
+ loading: 'On progress',
+ success: Project Unarchived ,
+ error: unable to Unarchive ,
+ });
+ } else {
+ toast.promise(func(), {
+ loading: 'On progress',
+ success: Project archived ,
+ error: Unable to arhive ,
+ });
+ }
+ }
+ };
+
+ const DeleteHandler = async () => {
+ if (token && orgName) {
+ const func = async () => {
+ const res = await deleteProject(token, projectName, orgName);
+ };
+ toast.promise(func(), {
+ loading: 'On progress',
+ success: Successfully deleted ,
+ error: Unable to delete ,
+ });
+ }
+ };
+ useEffect(() => {
+ fetchProjectData();
+ fetchProjectMembers();
+ }, [userContext?.setUsername, userContext?.setUserOrgs]);
+
+ return (
+
+
+
+ {pin&& }
+
+
{projectName}
+
{project ? project.description : <>>}
+
+ {(userContext?.userOrgs?.userOrgs[orgName].role === 'admin' ||
+ userContext?.userOrgs?.userOrgs[orgName].role === 'manager') && (
+ <>
+
+
setShowPopUp(showPopUp ? false : true)}
+ >
+
+
+
+
+ {pin ? 'Unpin' : 'Pin'}
+
+
+ {archive ? 'Unarchive' : 'Archive'}
+
+
navigate(`/editProject/${orgName}/${projectName}`)}
+ >
+ Edit
+
+
+ Delete
+
+
+ >
+ )}
+
+
+ Pull Requests
+ {githubData ? githubData.pulls : <>>}
+
+
+ Commits
+ {githubData ? githubData.commits : <>>}
+
+
+ Issues
+ {githubData ? githubData.issues : <>>}
+
+
+
+ {orgName && !orgName.endsWith('-userspace') && (
+
navigate(`/projectMembers/${orgName}/${projectName}`)}
+ >
+ {projectMembers && projectMembers.length > 0 ? (
+ projectMembers.slice(0, 4).map((obj) => {
+ const url =
+ AVATAR_URL + '/' + obj.key + '.png?apikey=' + AVATAR_API;
+ return
;
+ })
+ ) : (
+ <>
+
add Members
+ >
+ )}
+
+ )}
+ {orgName && !orgName.endsWith('-userspace') && (
+
+
navigate(`/project/${orgName}/${projectName}`)}
+ alt=''
+ />
+
+ )}
+
+ );
+};
+
+export default ProjectCard;
diff --git a/src/features/home/components/projectCardContainer/index.scss b/src/features/workspace/components/projectCardContainer/index.scss
similarity index 70%
rename from src/features/home/components/projectCardContainer/index.scss
rename to src/features/workspace/components/projectCardContainer/index.scss
index a3d0b26..d392ece 100644
--- a/src/features/home/components/projectCardContainer/index.scss
+++ b/src/features/workspace/components/projectCardContainer/index.scss
@@ -1,4 +1,5 @@
.projectcard-cont {
+ width: 100%;
display: grid;
grid-template-columns: repeat(2, 1fr);
row-gap: 2em;
@@ -13,3 +14,9 @@
grid-template-columns: 1fr;
}
}
+
+@media only screen and (max-width: 860px) {
+ .projectcard-cont {
+ grid-template-columns: 1fr;
+ }
+}
diff --git a/src/features/workspace/components/projectCardContainer/index.tsx b/src/features/workspace/components/projectCardContainer/index.tsx
new file mode 100644
index 0000000..bd64cd2
--- /dev/null
+++ b/src/features/workspace/components/projectCardContainer/index.tsx
@@ -0,0 +1,60 @@
+import React, { useEffect, useState } from 'react';
+import ProjectCard from '../projectCard';
+import './index.scss';
+import { Projects } from 'app/api/organization';
+import { ProjectsGithubData } from 'app/api/githubData';
+import { useSelector } from 'react-redux';
+
+interface Props {
+ weekly: boolean;
+ orgName: string;
+ orgProjects: Projects | null;
+ monthlyOrgProjectsData: ProjectsGithubData | null;
+ weeklyOrgProjectsData: ProjectsGithubData | null;
+ archives: boolean;
+}
+
+const ProjectCardCont: React.FC = ({
+ weekly,
+ orgName,
+ orgProjects,
+ monthlyOrgProjectsData,
+ weeklyOrgProjectsData,
+ archives,
+}) => {
+ const searchValue = useSelector((state: any) => state.searchKeyword.value);
+
+ useEffect(() => {}, [weekly, searchValue]);
+
+ return (
+ <>
+
+ {orgProjects &&
+ Object.entries(orgProjects)
+ .filter(([key, value]) => {
+ if (key.toLowerCase().includes(searchValue.toLowerCase()))
+ return [key, value];
+ })
+ .map(([key, value]) => {
+ return (
+ archives === value.archeive && (
+
+ )
+ );
+ })}
+
+ >
+ );
+};
+
+export default ProjectCardCont;
diff --git a/src/features/home/index.scss b/src/features/workspace/index.scss
similarity index 52%
rename from src/features/home/index.scss
rename to src/features/workspace/index.scss
index 3fcdedb..899d134 100644
--- a/src/features/home/index.scss
+++ b/src/features/workspace/index.scss
@@ -2,6 +2,7 @@
display: flex;
justify-content: space-between;
align-items: center;
+ gap: 0.5rem;
padding-left: 1.3em;
margin-top: 0.8em;
margin-bottom: 0.8em;
@@ -19,21 +20,54 @@
flex-grow: 1;
}
-.loader{
+.loader {
width: 10px;
height: 10px;
position: absolute;
top: 50%;
left: 50%;
-
}
-.loader-container{
+.loader-container {
width: 50px;
height: 50px;
position: relative;
padding: 50px;
}
+.home-header button {
+ border: none;
+ outline: none;
+ margin-right: 1em;
+ padding: 0.7em;
+ padding-left: 1.5em;
+ padding-right: 1.5em;
+ border-radius: 2em;
+ color: white;
+ font-size: 0.9rem;
+ background: var(--timerange-switch-btn-bg);
+
+}
+
+.loader-container {
+ position: absolute;
+ top: 0;
+ left: 0;
+ background-color: rgba(0, 0, 0, 0.31);
+ z-index: 1;
+ width: 100vw;
+ height: 100vh;
+ padding: 0px;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ .loading {
+ margin: 0;
+ padding: 0;
+ height: 4rem;
+ width: 4rem;
+ }
+}
@media only screen and (max-width: 860px) {
.home-header {
@@ -54,5 +88,3 @@
width: 90%;
}
}
-
-
diff --git a/src/features/workspace/index.tsx b/src/features/workspace/index.tsx
new file mode 100644
index 0000000..40ae173
--- /dev/null
+++ b/src/features/workspace/index.tsx
@@ -0,0 +1,143 @@
+import React, { useContext, useEffect, useState } from 'react';
+import SearchBar from 'app/components/search';
+import TimeRangeSwitch from 'app/components/timeRangeSwitch';
+import ProjectCardCont from './components/projectCardContainer';
+import LeaderBoard from './components/leaderboard';
+import './index.scss';
+import { useNavigate, useParams } from 'react-router-dom';
+import { getOrgProjects } from 'app/api/organization';
+import {
+ getOrgGithubData,
+ getOrgRank,
+} from 'app/api/githubData';
+import { Projects } from 'app/api/organization';
+import { ProjectsGithubData } from 'app/api/githubData';
+import { Contributors } from 'app/api/githubData';
+import loader from '../../app/assets/gifs/loader.gif';
+import UserContext from 'app/context/user/userContext';
+
+
+const Workspace = () => {
+ const navigate = useNavigate();
+ const token = localStorage.getItem('token');
+ const userContext = useContext(UserContext);
+ const [weekly, setWeekly] = useState(true);
+ const [orgProjects, setOrgProjects] = useState(null);
+ const [archives, setArcheives] = useState(false);
+ const [monthlyOrgRank, setMonthlyOrgRank] = useState(
+ null
+ );
+ const [weeklyOrgRank, setWeeklyOrgRank] = useState(null);
+ const [monthlyOrgProjectsData, setMOnthyOrgProjectsData] =
+ useState(null);
+ const [weeklyOrgProjectsData, setWeeklyOrgProjectsData] =
+ useState(null);
+ const { spaceName } = useParams();
+ const [isLoading, setIsLoading] = useState(true);
+ const fetchOrgProjects = async () => {
+ setIsLoading(true);
+ if (token && spaceName) {
+ try {
+ const orgProjects = await getOrgProjects(token, spaceName);
+ setOrgProjects(orgProjects.data.projects);
+ } catch (e) {
+ navigate('/');
+ }
+ }
+ setIsLoading(false);
+ };
+
+ const fetchWeeklyData = async () => {
+ try {
+ if (token && spaceName) {
+ const weeklyOrgRank = await getOrgRank(token, spaceName, true);
+ const weeklyOrgProjectsData = await getOrgGithubData(
+ token,
+ spaceName,
+ false
+ );
+
+ setWeeklyOrgProjectsData(weeklyOrgProjectsData.data.projects);
+ setWeeklyOrgRank(weeklyOrgRank.data.contributors);
+ }
+ } catch (e) {}
+ };
+
+ const fetchMonthlyData = async () => {
+ try {
+ if (token && spaceName) {
+ const monthlyOrgRank = await getOrgRank(token, spaceName, true);
+ const monthlyOrgProjectsData = await getOrgGithubData(
+ token,
+ spaceName,
+ true
+ );
+ setMonthlyOrgRank(monthlyOrgRank.data.contributors);
+ setMOnthyOrgProjectsData(monthlyOrgProjectsData.data.projects);
+ }
+ } catch (e) {}
+ };
+
+ useEffect(() => {
+ fetchOrgProjects();
+ fetchWeeklyData();
+ fetchMonthlyData();
+ }, [weekly, userContext?.setUsername, userContext?.setUserOrgs]);
+
+ return (
+ <>
+
+
+ navigate('/')}
+ >
+ Home
+
+ setArcheives(!archives)}
+ >
+ Archives
+
+ {spaceName &&
+ (userContext?.userOrgs?.userOrgs[spaceName].role == 'admin' ||
+ userContext?.userOrgs?.userOrgs[spaceName].role == 'manager') && (
+ navigate(`/addProject/${spaceName}`)}
+ >
+ Add Project
+
+ )}
+
+
+
+ {isLoading ? (
+
+
+
+ ) : (
+
+ {spaceName && (
+
+ )}
+
+
+ )}
+ >
+ );
+};
+
+export default Workspace;
diff --git a/src/features/home/slices/projectSearchSlice.ts b/src/features/workspace/slices/projectSearchSlice.ts
similarity index 84%
rename from src/features/home/slices/projectSearchSlice.ts
rename to src/features/workspace/slices/projectSearchSlice.ts
index ff31d91..4bbee7b 100644
--- a/src/features/home/slices/projectSearchSlice.ts
+++ b/src/features/workspace/slices/projectSearchSlice.ts
@@ -7,7 +7,7 @@ export const searchSlice = createSlice({
value: '',
},
reducers: {
- searchAction: (state, action: PayloadAction) => {
+ searchAction: (state: any, action: PayloadAction) => {
state.value = action.payload;
},
},
diff --git a/src/index.tsx b/src/index.tsx
index 062718b..f4dc382 100644
--- a/src/index.tsx
+++ b/src/index.tsx
@@ -6,23 +6,24 @@ import { BrowserRouter } from 'react-router-dom';
import { store } from 'app/state/store';
import { Provider } from 'react-redux';
import { QueryClient, QueryClientProvider } from 'react-query';
+import UserState from 'app/context/user/userState';
-
-const queryClient= new QueryClient()
+const queryClient = new QueryClient();
const root = ReactDOM.createRoot(
document.getElementById('root') as HTMLElement
);
-
root.render(
-
-
-
-
-
+
+
+
+
+
+
+
);