diff --git a/apps/api/migrations/versions/040ccb1d456e_add_thumbnail_image_to_orgs.py b/apps/api/migrations/versions/040ccb1d456e_add_thumbnail_image_to_orgs.py new file mode 100644 index 00000000..14895852 --- /dev/null +++ b/apps/api/migrations/versions/040ccb1d456e_add_thumbnail_image_to_orgs.py @@ -0,0 +1,30 @@ +"""Add thumbnail image to orgs + +Revision ID: 040ccb1d456e +Revises: 83b6d9d6f57a +Create Date: 2024-09-27 10:23:50.508031 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa # noqa: F401 +import sqlmodel # noqa: F401 + +# revision identifiers, used by Alembic. +revision: str = '040ccb1d456e' +down_revision: Union[str, None] = '83b6d9d6f57a' +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.add_column('organization', sa.Column('thumbnail_image', sa.String(), nullable=True)) + # ### end Alembic commands ### + + +def downgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.drop_column('organization', 'thumbnail_image') + # ### end Alembic commands ### diff --git a/apps/api/src/db/organizations.py b/apps/api/src/db/organizations.py index 0edf1200..f5702103 100644 --- a/apps/api/src/db/organizations.py +++ b/apps/api/src/db/organizations.py @@ -12,6 +12,7 @@ class OrganizationBase(SQLModel): slug: str email: str logo_image: Optional[str] + thumbnail_image: Optional[str] class Organization(OrganizationBase, table=True): diff --git a/apps/api/src/routers/orgs.py b/apps/api/src/routers/orgs.py index 748b5fa6..5c171f62 100644 --- a/apps/api/src/routers/orgs.py +++ b/apps/api/src/routers/orgs.py @@ -38,6 +38,7 @@ from src.services.orgs.orgs import ( update_org, update_org_logo, update_org_signup_mechanism, + update_org_thumbnail, ) @@ -303,7 +304,7 @@ async def api_update_org_logo( db_session: Session = Depends(get_db_session), ): """ - Get single Org by Slug + Update org logo """ return await update_org_logo( request=request, @@ -314,6 +315,26 @@ async def api_update_org_logo( ) +@router.put("/{org_id}/thumbnail") +async def api_update_org_thumbnail( + request: Request, + org_id: str, + thumbnail_file: UploadFile, + current_user: PublicUser = Depends(get_current_user), + db_session: Session = Depends(get_db_session), +): + """ + Update org thumbnail + """ + return await update_org_thumbnail( + request=request, + thumbnail_file=thumbnail_file, + org_id=org_id, + current_user=current_user, + db_session=db_session, + ) + + @router.get("/user/page/{page}/limit/{limit}") async def api_user_orgs( request: Request, diff --git a/apps/api/src/services/orgs/orgs.py b/apps/api/src/services/orgs/orgs.py index 23d1b4e6..c7e92000 100644 --- a/apps/api/src/services/orgs/orgs.py +++ b/apps/api/src/services/orgs/orgs.py @@ -34,9 +34,10 @@ from src.db.organizations import ( OrganizationRead, OrganizationUpdate, ) -from src.services.orgs.logos import upload_org_logo from fastapi import HTTPException, UploadFile, status, Request +from src.services.orgs.uploads import upload_org_logo, upload_org_thumbnail + async def get_organization( request: Request, @@ -421,6 +422,42 @@ async def update_org_logo( return {"detail": "Logo updated"} +async def update_org_thumbnail( + request: Request, + thumbnail_file: UploadFile, + org_id: str, + current_user: PublicUser | AnonymousUser, + db_session: Session, +): + statement = select(Organization).where(Organization.id == org_id) + result = db_session.exec(statement) + + org = result.first() + + if not org: + raise HTTPException( + status_code=404, + detail="Organization not found", + ) + + # RBAC check + await rbac_check(request, org.org_uuid, current_user, "update", db_session) + + # Upload logo + name_in_disk = await upload_org_thumbnail(thumbnail_file, org.org_uuid) + + # Update org + org.thumbnail_image = name_in_disk + + # Complete the org object + org.update_date = str(datetime.now()) + + db_session.add(org) + db_session.commit() + db_session.refresh(org) + + return {"detail": "Thumbnail updated"} + async def delete_org( request: Request, diff --git a/apps/api/src/services/orgs/logos.py b/apps/api/src/services/orgs/uploads.py similarity index 54% rename from apps/api/src/services/orgs/logos.py rename to apps/api/src/services/orgs/uploads.py index 9bb173d3..813b625d 100644 --- a/apps/api/src/services/orgs/logos.py +++ b/apps/api/src/services/orgs/uploads.py @@ -16,3 +16,18 @@ async def upload_org_logo(logo_file, org_uuid): ) return name_in_disk + + +async def upload_org_thumbnail(thumbnail_file, org_uuid): + contents = thumbnail_file.file.read() + name_in_disk = f"{uuid4()}.{thumbnail_file.filename.split('.')[-1]}" + + await upload_content( + "thumbnails", + "orgs", + org_uuid, + contents, + name_in_disk, + ) + + return name_in_disk diff --git a/apps/web/app/orgs/[orgslug]/dash/org/settings/[subpage]/page.tsx b/apps/web/app/orgs/[orgslug]/dash/org/settings/[subpage]/page.tsx index ee6a9479..d748809b 100644 --- a/apps/web/app/orgs/[orgslug]/dash/org/settings/[subpage]/page.tsx +++ b/apps/web/app/orgs/[orgslug]/dash/org/settings/[subpage]/page.tsx @@ -3,7 +3,7 @@ import BreadCrumbs from '@components/Dashboard/UI/BreadCrumbs' import { getUriWithOrg } from '@services/config/config' import { Info } from 'lucide-react' import Link from 'next/link' -import React from 'react' +import React, { useEffect } from 'react' import { motion } from 'framer-motion' import OrgEditGeneral from '@components/Dashboard/Org/OrgEditGeneral/OrgEditGeneral' @@ -13,14 +13,31 @@ export type OrgParams = { } function OrgPage({ params }: { params: OrgParams }) { + const [H1Label, setH1Label] = React.useState('') + const [H2Label, setH2Label] = React.useState('') + + function handleLabels() { + if (params.subpage == 'general') { + setH1Label('General') + setH2Label('Manage your organization settings') + } + } + + useEffect(() => { + handleLabels() + }, [params.subpage, params]) + return (