diff --git a/apps/api/src/routers/auth.py b/apps/api/src/routers/auth.py
index ff7ea5c6..f13bc033 100644
--- a/apps/api/src/routers/auth.py
+++ b/apps/api/src/routers/auth.py
@@ -4,11 +4,11 @@ from fastapi import Depends, APIRouter, HTTPException, Response, status, Request
from fastapi.security import OAuth2PasswordRequestForm
from pydantic import BaseModel, EmailStr
from sqlmodel import Session
-from src.db.users import AnonymousUser, PublicUser, UserRead
+from src.db.users import AnonymousUser, UserRead
from src.core.events.database import get_db_session
from config.config import get_learnhouse_config
from src.security.auth import AuthJWT, authenticate_user, get_current_user
-from src.services.auth.utils import get_google_user_info, signWithGoogle
+from src.services.auth.utils import signWithGoogle
router = APIRouter()
diff --git a/apps/api/src/routers/orgs.py b/apps/api/src/routers/orgs.py
index cd7f7d9b..ad345649 100644
--- a/apps/api/src/routers/orgs.py
+++ b/apps/api/src/routers/orgs.py
@@ -8,6 +8,7 @@ from src.services.orgs.invites import (
get_invite_code,
get_invite_codes,
)
+from src.services.orgs.join import JoinOrg, join_org
from src.services.orgs.users import (
get_list_of_invited_users,
get_organization_users,
@@ -99,6 +100,19 @@ async def api_get_org_users(
return await get_organization_users(request, org_id, db_session, current_user)
+@router.post("/join")
+async def api_join_an_org(
+ request: Request,
+ args: JoinOrg,
+ current_user: PublicUser = Depends(get_current_user),
+ db_session: Session = Depends(get_db_session),
+):
+ """
+ Get single Org by ID
+ """
+ return await join_org(request, args, current_user, db_session)
+
+
@router.put("/{org_id}/users/{user_id}/role/{role_uuid}")
async def api_update_user_role(
request: Request,
diff --git a/apps/api/src/routers/users.py b/apps/api/src/routers/users.py
index 5dc621f3..98cb25aa 100644
--- a/apps/api/src/routers/users.py
+++ b/apps/api/src/routers/users.py
@@ -85,7 +85,6 @@ async def api_create_user_with_orgid(
"""
Create User with Org ID
"""
- print(await get_org_join_mechanism(request, org_id, current_user, db_session))
# TODO(fix) : This is temporary, logic should be moved to service
if (
diff --git a/apps/api/src/services/orgs/join.py b/apps/api/src/services/orgs/join.py
new file mode 100644
index 00000000..177cc04e
--- /dev/null
+++ b/apps/api/src/services/orgs/join.py
@@ -0,0 +1,119 @@
+from datetime import datetime
+from typing import Optional
+from fastapi import HTTPException, Request
+from pydantic import BaseModel
+from sqlmodel import Session, select
+from src.db.organizations import Organization
+from src.db.user_organizations import UserOrganization
+from src.db.users import AnonymousUser, PublicUser, User
+from src.services.orgs.invites import get_invite_code
+from src.services.orgs.orgs import get_org_join_mechanism
+
+
+class JoinOrg(BaseModel):
+ org_id: int
+ user_id: str
+ invite_code: Optional[str]
+
+
+async def join_org(
+ request: Request,
+ args: JoinOrg,
+ current_user: PublicUser | AnonymousUser,
+ db_session: Session,
+):
+ statement = select(Organization).where(Organization.id == args.org_id)
+ result = db_session.exec(statement)
+
+ org = result.first()
+
+ if not org:
+ raise HTTPException(
+ status_code=404,
+ detail="Organization not found",
+ )
+
+ join_method = await get_org_join_mechanism(
+ request, args.org_id, current_user, db_session
+ )
+
+ # Get User
+ statement = select(User).where(User.id == args.user_id)
+ result = db_session.exec(statement)
+
+ user = result.first()
+
+ # Check if User isn't already part of the org
+ statement = select(UserOrganization).where(
+ UserOrganization.user_id == args.user_id, UserOrganization.org_id == args.org_id
+ )
+ result = db_session.exec(statement)
+
+ userorg = result.first()
+
+ if userorg:
+ raise HTTPException(
+ status_code=400, detail="User is already part of that organization"
+ )
+
+ if join_method == "inviteOnly" and user and org and args.invite_code:
+ if user.id is not None and org.id is not None:
+
+ # Check if invite code exists
+ inviteCode = await get_invite_code(
+ request, org.id, args.invite_code, current_user, db_session
+ )
+
+ if not inviteCode:
+ raise HTTPException(
+ status_code=400,
+ detail="Invite code is incorrect",
+ )
+
+ # Link user and organization
+ user_organization = UserOrganization(
+ user_id=user.id,
+ org_id=org.id,
+ role_id=3,
+ creation_date=str(datetime.now()),
+ update_date=str(datetime.now()),
+ )
+
+ db_session.add(user_organization)
+ db_session.commit()
+
+ return "Great, You're part of the Organization"
+
+ else:
+ raise HTTPException(
+ status_code=403,
+ detail="Something wrong, try later.",
+ )
+
+ if join_method == "open" and user and org:
+ if user.id is not None and org.id is not None:
+ # Link user and organization
+ user_organization = UserOrganization(
+ user_id=user.id,
+ org_id=org.id,
+ role_id=3,
+ creation_date=str(datetime.now()),
+ update_date=str(datetime.now()),
+ )
+
+ db_session.add(user_organization)
+ db_session.commit()
+
+ return "Great, You're part of the Organization"
+
+ else:
+ raise HTTPException(
+ status_code=403,
+ detail="Something wrong, try later.",
+ )
+
+ else:
+ raise HTTPException(
+ status_code=403,
+ detail="Something wrong, try later.",
+ )
diff --git a/apps/api/src/services/orgs/orgs.py b/apps/api/src/services/orgs/orgs.py
index bd96d22b..d7923ae9 100644
--- a/apps/api/src/services/orgs/orgs.py
+++ b/apps/api/src/services/orgs/orgs.py
@@ -436,8 +436,7 @@ async def get_orgs_by_user(
orgs = result.all()
- return orgs
-
+ return orgs #type:ignore
# Config related
async def update_org_signup_mechanism(
diff --git a/apps/web/app/auth/signup/signup.tsx b/apps/web/app/auth/signup/signup.tsx
index 3eb1b2e2..933fd53f 100644
--- a/apps/web/app/auth/signup/signup.tsx
+++ b/apps/web/app/auth/signup/signup.tsx
@@ -16,6 +16,8 @@ import { validateInviteCode } from '@services/organizations/invites'
import PageLoading from '@components/Objects/Loaders/PageLoading'
import Toast from '@components/StyledElements/Toast/Toast'
import toast from 'react-hot-toast'
+import { BarLoader } from 'react-spinners'
+import { joinOrg } from '@services/organizations/orgs'
interface SignUpClientProps {
org: any
@@ -97,7 +99,7 @@ function SignUpClient(props: SignUpClientProps) {
{joinMethod == 'inviteOnly' &&
(inviteCode ? (
session.status == 'authenticated' ? (
-
+
) : (
)
@@ -112,7 +114,30 @@ function SignUpClient(props: SignUpClientProps) {
const LoggedInJoinScreen = (props: any) => {
const session = useLHSession() as any
const org = useOrg() as any
+ const invite_code = props.inviteCode
const [isLoading, setIsLoading] = React.useState(true)
+ const [isSumbitting, setIsSubmitting] = React.useState(false)
+ const router = useRouter()
+
+ const join = async () => {
+ setIsSubmitting(true)
+ const res = await joinOrg({ org_id: org.id, user_id: session?.data?.user?.id, invite_code: props.inviteCode }, null, session.data?.tokens?.access_token)
+ //wait for 1s
+ if (res.success) {
+ toast.success(
+ res.data
+ )
+ setTimeout(() => {
+ router.push(`/`)
+ }, 2000)
+ setIsSubmitting(false)
+ } else {
+ toast.error(res.data.detail)
+ setIsLoading(false)
+ setIsSubmitting(false)
+ }
+
+ }
useEffect(() => {
if (session && org) {
@@ -122,6 +147,7 @@ const LoggedInJoinScreen = (props: any) => {
return (
+
Hi
@@ -131,9 +157,13 @@ const LoggedInJoinScreen = (props: any) => {
join {org?.name} ?
-
@@ -154,7 +184,7 @@ const NoTokenScreen = (props: any) => {
const validateCode = async () => {
setIsLoading(true)
- let res = await validateInviteCode(org?.id, inviteCode,session?.user?.tokens.access_token)
+ let res = await validateInviteCode(org?.id, inviteCode, session?.user?.tokens.access_token)
//wait for 1s
if (res.success) {
toast.success(
diff --git a/apps/web/app/orgs/[orgslug]/(withmenu)/course/[courseuuid]/error.tsx b/apps/web/app/orgs/[orgslug]/(withmenu)/course/[courseuuid]/error.tsx
index bb8f7f65..1e0708da 100644
--- a/apps/web/app/orgs/[orgslug]/(withmenu)/course/[courseuuid]/error.tsx
+++ b/apps/web/app/orgs/[orgslug]/(withmenu)/course/[courseuuid]/error.tsx
@@ -1,6 +1,5 @@
'use client' // Error components must be Client Components
-import ErrorUI from '@components/StyledElements/Error/Error'
import { useEffect } from 'react'
export default function Error({
diff --git a/apps/web/components/Contexts/CourseContext.tsx b/apps/web/components/Contexts/CourseContext.tsx
index 585f698a..a2f9f161 100644
--- a/apps/web/components/Contexts/CourseContext.tsx
+++ b/apps/web/components/Contexts/CourseContext.tsx
@@ -1,5 +1,4 @@
'use client'
-import PageLoading from '@components/Objects/Loaders/PageLoading'
import { getAPIUrl } from '@services/config/config'
import { swrFetcher } from '@services/utils/ts/requests'
import React, { createContext, useContext, useEffect, useReducer } from 'react'
diff --git a/apps/web/components/Contexts/OrgContext.tsx b/apps/web/components/Contexts/OrgContext.tsx
index 8296fc50..f434b3f1 100644
--- a/apps/web/components/Contexts/OrgContext.tsx
+++ b/apps/web/components/Contexts/OrgContext.tsx
@@ -1,7 +1,7 @@
'use client'
import { getAPIUrl, getUriWithoutOrg } from '@services/config/config'
import { swrFetcher } from '@services/utils/ts/requests'
-import React, { createContext, useContext, useEffect, useMemo, useState } from 'react'
+import React, { createContext, useContext, useMemo } from 'react'
import useSWR from 'swr'
import { useLHSession } from '@components/Contexts/LHSessionContext'
import ErrorUI from '@components/StyledElements/Error/Error'
diff --git a/apps/web/components/Dashboard/UI/LeftMenu.tsx b/apps/web/components/Dashboard/UI/LeftMenu.tsx
index ba951506..9f695bbc 100644
--- a/apps/web/components/Dashboard/UI/LeftMenu.tsx
+++ b/apps/web/components/Dashboard/UI/LeftMenu.tsx
@@ -6,7 +6,6 @@ import LearnHouseDashboardLogo from '@public/dashLogo.png'
import { BookCopy, Home, LogOut, School, Settings, Users } from 'lucide-react'
import Image from 'next/image'
import Link from 'next/link'
-import { useRouter } from 'next/navigation'
import React, { useEffect } from 'react'
import UserAvatar from '../../Objects/UserAvatar'
import AdminAuthorization from '@components/Security/AdminAuthorization'
diff --git a/apps/web/components/Hooks/useAdminStatus.tsx b/apps/web/components/Hooks/useAdminStatus.tsx
index 4a5ee5b3..93ae93d0 100644
--- a/apps/web/components/Hooks/useAdminStatus.tsx
+++ b/apps/web/components/Hooks/useAdminStatus.tsx
@@ -20,10 +20,12 @@ function useAdminStatus() {
const isAdminVar = userRoles.some((role: Role) => {
return (
role.org.id === org.id &&
- (role.role.id === 1 ||
+ (
+ role.role.id === 1 ||
role.role.id === 2 ||
role.role.role_uuid === 'role_global_admin' ||
- role.role.role_uuid === 'role_global_maintainer')
+ role.role.role_uuid === 'role_global_maintainer'
+ )
);
});
setIsAdmin(isAdminVar);
@@ -38,3 +40,4 @@ function useAdminStatus() {
}
export default useAdminStatus;
+
diff --git a/apps/web/components/Security/HeaderProfileBox.tsx b/apps/web/components/Security/HeaderProfileBox.tsx
index b64b45d7..ca0b9c50 100644
--- a/apps/web/components/Security/HeaderProfileBox.tsx
+++ b/apps/web/components/Security/HeaderProfileBox.tsx
@@ -7,11 +7,11 @@ import UserAvatar from '@components/Objects/UserAvatar'
import useAdminStatus from '@components/Hooks/useAdminStatus'
import { useLHSession } from '@components/Contexts/LHSessionContext'
import { useOrg } from '@components/Contexts/OrgContext'
-import { getUriWithOrg, getUriWithoutOrg } from '@services/config/config'
+import { getUriWithoutOrg } from '@services/config/config'
export const HeaderProfileBox = () => {
const session = useLHSession() as any
- const isUserAdmin = useAdminStatus() as any
+ const isUserAdmin = useAdminStatus()
const org = useOrg() as any
useEffect(() => { }
@@ -37,7 +37,7 @@ export const HeaderProfileBox = () => {
{session.data.user.username}
- {isUserAdmin &&
ADMIN
}
+ {isUserAdmin.isAdmin &&
ADMIN
}
diff --git a/apps/web/components/StyledElements/Info/Info.tsx b/apps/web/components/StyledElements/Info/Info.tsx
index 45492486..a117783a 100644
--- a/apps/web/components/StyledElements/Info/Info.tsx
+++ b/apps/web/components/StyledElements/Info/Info.tsx
@@ -1,6 +1,6 @@
'use client'
import { getUriWithoutOrg } from '@services/config/config'
-import { AlertTriangle, Diamond, Home, PersonStanding, RefreshCcw } from 'lucide-react'
+import { Diamond, Home, PersonStanding } from 'lucide-react'
import Link from 'next/link'
import React from 'react'
diff --git a/apps/web/middleware.ts b/apps/web/middleware.ts
index 47327727..2234ca51 100644
--- a/apps/web/middleware.ts
+++ b/apps/web/middleware.ts
@@ -8,7 +8,6 @@ import {
} from './services/config/config'
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
-import path from 'path'
export const config = {
matcher: [
diff --git a/apps/web/services/blocks/Image/images.ts b/apps/web/services/blocks/Image/images.ts
index 454d87fa..65331e91 100644
--- a/apps/web/services/blocks/Image/images.ts
+++ b/apps/web/services/blocks/Image/images.ts
@@ -1,7 +1,5 @@
import { getAPIUrl } from '@services/config/config'
import {
- RequestBody,
- RequestBodyForm,
RequestBodyFormWithAuthHeader,
RequestBodyWithAuthHeader,
} from '@services/utils/ts/requests'
diff --git a/apps/web/services/blocks/Pdf/pdf.ts b/apps/web/services/blocks/Pdf/pdf.ts
index 99c1e3f0..1f93ce5d 100644
--- a/apps/web/services/blocks/Pdf/pdf.ts
+++ b/apps/web/services/blocks/Pdf/pdf.ts
@@ -1,7 +1,5 @@
import { getAPIUrl } from '@services/config/config'
import {
- RequestBody,
- RequestBodyForm,
RequestBodyFormWithAuthHeader,
RequestBodyWithAuthHeader,
} from '@services/utils/ts/requests'
diff --git a/apps/web/services/blocks/Quiz/quiz.ts b/apps/web/services/blocks/Quiz/quiz.ts
index 5d4eb093..b9acae06 100644
--- a/apps/web/services/blocks/Quiz/quiz.ts
+++ b/apps/web/services/blocks/Quiz/quiz.ts
@@ -1,5 +1,5 @@
import { getAPIUrl } from '@services/config/config'
-import { RequestBody, RequestBodyWithAuthHeader } from '@services/utils/ts/requests'
+import { RequestBodyWithAuthHeader } from '@services/utils/ts/requests'
export async function submitQuizBlock(activity_id: string, data: any,access_token:string) {
const result: any = await fetch(
diff --git a/apps/web/services/blocks/Video/video.ts b/apps/web/services/blocks/Video/video.ts
index ff8e9bd6..6082b080 100644
--- a/apps/web/services/blocks/Video/video.ts
+++ b/apps/web/services/blocks/Video/video.ts
@@ -1,7 +1,5 @@
import { getAPIUrl } from '@services/config/config'
import {
- RequestBody,
- RequestBodyForm,
RequestBodyFormWithAuthHeader,
RequestBodyWithAuthHeader,
} from '@services/utils/ts/requests'
diff --git a/apps/web/services/organizations/orgs.ts b/apps/web/services/organizations/orgs.ts
index e8cd4771..9ec5bd1a 100644
--- a/apps/web/services/organizations/orgs.ts
+++ b/apps/web/services/organizations/orgs.ts
@@ -13,7 +13,7 @@ import {
export async function createNewOrganization(body: any, access_token: string) {
const result = await fetch(
`${getAPIUrl()}orgs/`,
- RequestBodyWithAuthHeader('POST', body, null,access_token)
+ RequestBodyWithAuthHeader('POST', body, null, access_token)
)
const res = await errorHandling(result)
return res
@@ -113,3 +113,20 @@ export async function removeUserFromOrg(
const res = await getResponseMetadata(result)
return res
}
+
+export async function joinOrg(
+ args: {
+ org_id: number
+ user_id: string
+ invite_code?: string
+ },
+ next: any,
+ access_token?: string
+) {
+ const result = await fetch(
+ `${getAPIUrl()}orgs/join`,
+ RequestBodyWithAuthHeader('POST', args, next, access_token)
+ )
+ const res = await getResponseMetadata(result)
+ return res
+}