Skip to content

Commit

Permalink
feat: public profiles for users and organisations (#276)
Browse files Browse the repository at this point in the history
* feat public profile for organisation and user

Signed-off-by: karan <[email protected]>

* fix: reviewed changes

Signed-off-by: karan <[email protected]>

* fix: changes required

Signed-off-by: karan <[email protected]>

---------

Signed-off-by: karan <[email protected]>
  • Loading branch information
16-karan authored Sep 18, 2023
1 parent 9446b83 commit d2d8b3a
Show file tree
Hide file tree
Showing 16 changed files with 1,081 additions and 1 deletion.
42 changes: 41 additions & 1 deletion src/api/organization.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { axiosGet, axiosPost, axiosPut } from "../services/apiRequests"
import { axiosGet, axiosPost, axiosPublicUserGet, axiosPut } from "../services/apiRequests"

import { apiRoutes } from "../config/apiRoutes";
import { getFromLocalStorage } from "./Auth";
Expand Down Expand Up @@ -302,4 +302,44 @@ export const createConnection = async (orgName: string) => {
}
}

// public profile

export const getPublicUsers = async (pageNumber: number, pageSize: number, search :string) => {

const url = `${apiRoutes.public.users}?pageNumber=${pageNumber}&pageSize=${pageSize}&search=${search}`

const axiosPayload = {
url,
}

try {
return await axiosPublicUserGet(axiosPayload);
}
catch (error) {
const err = error as Error
return err?.message
}
}

export const getPublicOrganizations = async (pageNumber: number, pageSize: number, search :string) => {

const url = `${apiRoutes.public.organizations}?pageNumber=${pageNumber}&pageSize=${pageSize}&search=${search}`

const config = {
headers: {
'Content-Type': 'application/json',
}
}
const axiosPayload = {
url,
config
}

try {
return await axiosGet(axiosPayload);
}
catch (error) {
const err = error as Error
return err?.message
}
}
16 changes: 16 additions & 0 deletions src/app/PublicProfileLayout.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
---
import NavBarSidebar from "./NavBarSidebar.astro";
---

<NavBarSidebar/>
<div class="flex pt-16 overflow-hidden bg-gray-50 dark:bg-gray-900">
<div
id="main-content"
class="relative w-full h-full overflow-y-auto bg-gray-50 lg:ml-64 dark:bg-gray-900 min-h-screen flex justify-between flex-col"
style="min-height: calc(100vh - 64px);"
>
<slot />

</div>
</div>
83 changes: 83 additions & 0 deletions src/components/publicProfile/OrgUserInfoLayout.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
---
interface UserDetails {
lastName: string;
firstName: string;
email: string;
publicProfile: boolean;
id: number;
username: string;
}
const { orgUsersData } = Astro.props;
---
{orgUsersData &&
<div class="grid lg:grid-cols-2 sm:grid-cols-1 gap-2 rounded">
<div
class="px-6 py-4 col-span-1 bg-gradient-to-r from-white via-white to-gray-50 hover:bg-gradient-to-br focus:ring-4 focus:outline-none focus:ring-gray-300 dark:focus:ring-gray-800 shadow-lg shadow-gray-500/50 dark:shadow-lg dark:shadow-gray-800/80 rounded-lg"
>
{
orgUsersData &&
orgUsersData?.map((orgUser: UserDetails) => {
return (
<>
<div class=" flex">
{orgUser.firstName &&
<svg
class="w-6 h-6 text-gray-800 dark:text-white"
aria-hidden="true"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 20 20"
>
<path
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M10 19a9 9 0 1 0 0-18 9 9 0 0 0 0 18Zm0 0a8.949 8.949 0 0 0 4.951-1.488A3.987 3.987 0 0 0 11 14H9a3.987 3.987 0 0 0-3.951 3.512A8.948 8.948 0 0 0 10 19Zm3-11a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z"
/>
</svg>














<div class="font-bold text-xl pl-2 mb-2">
{orgUser.firstName} {orgUser.lastName}
</div>}
</div>

<div class="flex">
{orgUser.email &&
<svg
class="w-6 h-6 text-gray-800 dark:text-white m-0.5"
aria-hidden="true"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 20 16"
>
<path
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="m19 2-8.4 7.05a1 1 0 0 1-1.2 0L1 2m18 0a1 1 0 0 0-1-1H2a1 1 0 0 0-1 1m18 0v12a1 1 0 0 1-1 1H2a1 1 0 0 1-1-1V2"
/>
</svg>
<p class="text-gray-700 text-xl mb-1.5 pl-1">{orgUser.email}</p>
}
</div>
</>
);
})
}
</div>
</div>}
159 changes: 159 additions & 0 deletions src/components/publicProfile/OrganisationPublicProfile.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
'use client';

import { ChangeEvent, useEffect, useState } from 'react';
import { getPublicOrganizations } from '../../api/organization';
import type { AxiosResponse } from 'axios';
import { apiStatusCodes } from '../../config/CommonConstant';
import SearchInput from '../SearchInput';
import { Button, Card, Pagination } from 'flowbite-react';
import CustomSpinner from '../CustomSpinner';
import CustomAvatar from '../Avatar';
import { EmptyListMessage } from '../EmptyListComponent';

const OrganisationPublicProfile = () => {
const initialPageState = {
pageNumber: 1,
pageSize: 10,
total: 0,
};

const [organizationsList, setOrganizationList] = useState([]);

const [loading, setLoading] = useState<boolean>(true);
const [error, setError] = useState<string | null>(null);
const [searchText, setSearchText] = useState('');
const [currentPage, setCurrentPage] = useState(initialPageState);
const onPageChange = (page: number) => {
setCurrentPage({
...currentPage,
pageNumber: page,
});
};

const getAllOrganizations = async () => {
setLoading(true);
const response = await getPublicOrganizations(
currentPage?.pageNumber,
currentPage?.pageSize,
searchText,
);

const { data } = response as AxiosResponse;

if (data?.statusCode === apiStatusCodes?.API_STATUS_SUCCESS) {
const totalPages = data?.data?.totalPages;

const orgList = data?.data?.organizations.map((userOrg: any) => {
return userOrg;
});

setOrganizationList(orgList);
setCurrentPage({
...currentPage,
total: totalPages,
});
} else {
setError(response as string);
}
setLoading(false);
};

useEffect(() => {
let getData: NodeJS.Timeout;

if (searchText?.length >= 1) {
getData = setTimeout(() => {
getAllOrganizations();
}, 1000);
} else {
getAllOrganizations();
}

return () => clearTimeout(getData);
}, [searchText, currentPage.pageNumber]);

const searchInputChange = (e: ChangeEvent<HTMLInputElement>) => {
setSearchText(e.target.value);
};

return (
<div>
<div className="flex items-center justify-between mb-4 p-2 pl-0">
<SearchInput onInputChange={searchInputChange} />
</div>

<div className="flex flex-wrap">
{loading ? (
<div className="flex items-center justify-center mb-4 ">
<CustomSpinner />
</div>
) : organizationsList && organizationsList?.length > 0 ? (
<div className="mt-1 grid w-full grid-cols-1 gap-4 mt-0 mb-4 xl:grid-cols-2">
{organizationsList?.map(
(org: {
logoUrl: string;
name: string;
description: string;
id: number;
orgSlug: string;
}) => (
<Card
onClick={() => {
window.location.href = `/org/${org?.orgSlug}`;
}}
className="transform transition duration-500 hover:scale-[1.02] hover:bg-gray-50 cursor-pointer"
>
<div className="flex items-center">
{org.logoUrl ? (
<CustomAvatar size="80" src={org?.logoUrl} />
) : (
<CustomAvatar size="80" name={org?.name} />
)}

<div className="ml-4">
<h5 className="text-xl font-bold tracking-tight text-gray-900 dark:text-white">
<p>{org?.name}</p>
</h5>
<div className="flow-root h-auto">
<ul className="divide-y divide-gray-200 dark:divide-gray-700">
<li className="py-3 sm:py-4 overflow-auto">
<div className="flex items-center space-x-4">
<div className="inline-flex items-center text-base text-lg text-gray-900 dark:text-white">
{org?.description}
</div>
</div>
</li>
</ul>
</div>
</div>
</div>
</Card>
),
)}
</div>
) : (
organizationsList && (
<div className="flex justify-center items-center">
<EmptyListMessage
message={'No Matching Organization'}
description={''}
/>
</div>
)
)}

<div className="flex items-center justify-end mb-4">
{organizationsList && organizationsList?.length > 0 && (
<Pagination
currentPage={currentPage?.pageNumber}
onPageChange={onPageChange}
totalPages={currentPage?.total}
/>
)}
</div>
</div>
</div>
);
};

export default OrganisationPublicProfile;
30 changes: 30 additions & 0 deletions src/components/publicProfile/ProfileDesign.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
---
const {orgData} = Astro.props
import CustomAvatar from "../../components/Avatar"
---


<div class="min-[320]:h-auto md:h-screen col-span-1 border-white box-border">
<div class="w-full h-full bg-white rounded-lg shadow dark:bg-gray-800 dark:border-gray-700 ">
<div class="flex flex-col items-center pb-10">

{orgData?.logoUrl ? (
<CustomAvatar className="my-4 rounded-full shadow-lg" size="100" src={orgData?.logoUrl} client:load />
) : (
<CustomAvatar className="my-4 rounded-full shadow-lg" size="180" name={orgData?.name} client:load />
)}

<h3 class="mb-1 text-3xl font-medium text-gray-900 dark:text-white pt-4">{orgData?.name}</h3>
<div class="flex text-center align-middle ">
{orgData?.website &&
<svg class="w-6 h-6 text-gray-800 dark:text-white" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 19 19">
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11.013 7.962a3.519 3.519 0 0 0-4.975 0l-3.554 3.554a3.518 3.518 0 0 0 4.975 4.975l.461-.46m-.461-4.515a3.518 3.518 0 0 0 4.975 0l3.553-3.554a3.518 3.518 0 0 0-4.974-4.975L10.3 3.7"/>
</svg>
<a href={orgData?.website}> <span class="text-2xl text-gray-500 dark:text-gray-400 pl-2 ">{orgData?.website}</span></a>
}
</div>
<p class="pt-2 p-4 flex items-center justify-center flex-wrap">{orgData?.description}</p>
</div>
</div>
</div>

Loading

0 comments on commit d2d8b3a

Please sign in to comment.