Skip to content

Commit

Permalink
Update
Browse files Browse the repository at this point in the history
  • Loading branch information
haiphucnguyen committed Dec 24, 2024
1 parent 84cf2e3 commit a36698c
Show file tree
Hide file tree
Showing 21 changed files with 134 additions and 131 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/node.js.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,5 +48,5 @@ jobs:
- run: pnpm install
- name: Build the application
env:
BACKEND_API: "http://localhost:8080"
BACKEND_URL: "http://localhost:8080"
run: pnpm build
13 changes: 7 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# FlowInquiry Client

[![Build status](https://github.com/flowinquiry/flowinquiry-frontend/actions/workflows/node.js.yml/badge.svg)](https://github.com/flowinquiry/flowinquiry-frontend/actions/workflows/node.js.yml)
![License](https://img.shields.io/badge/License-AGPLv3-blue)

## What is FlowInquiry

Expand All @@ -11,16 +12,16 @@ FlowInquiry is a service designed to streamline the management of cases, tickets
FlowInquiry addresses several challenges faced by organizations in managing cases, tickets, and team communication. Here are some specific use cases:

**On-Call System Management**
In an on-call system, teams often face challenges in managing incoming requests or incidents, particularly when multiple shifts or team members are involved. FlowInquiry ensures that each request follows a well-defined workflow, with SLAs for escalation and resolution. This helps reduce response times, avoids missed escalations, and provides clear accountability for handling incidents.
Managing incoming requests or incidents in an on-call system can be challenging with multiple shifts and team members. FlowInquiry streamlines the process by enforcing well-defined workflows with SLAs for escalation and resolution, reducing response times, avoiding missed escalations, and ensuring accountability.

**Case Management in CRM Applications**
CRM applications often struggle to manage customer cases effectively, especially when handling inquiries, complaints, or service requests. FlowInquiry enables teams to define custom workflows tailored to specific case types, such as refunds, escalations, or product inquiries. SLAs for each workflow stage ensure customers receive timely updates and resolutions, enhancing customer satisfaction and loyalty.
FlowInquiry helps CRM teams manage customer cases like inquiries, complaints, and service requests by enabling custom workflows tailored to case types such as refunds or escalations. SLAs at each workflow stage ensure timely updates and resolutions, boosting customer satisfaction and loyalty.

**Team Communication and Collaboration**
Effective communication within and across teams can be difficult in large organizations, especially when requests involve multiple departments or external stakeholders. FlowInquiry acts as a centralized platform where requests are logged, tracked, and routed through clearly defined workflows. This reduces miscommunication, prevents delays, and ensures all parties are aligned on priorities.
FlowInquiry enhances communication across teams by providing a centralized platform to log, track, and route requests through clear workflows. This minimizes miscommunication, prevents delays, and aligns all stakeholders on priorities, even in large organizations.

**Service Request Tracking for IT Teams**
IT teams managing internal service requests often encounter bottlenecks due to unclear processes or manual tracking. FlowInquiry allows IT departments to automate workflows for common requests such as software installation, access management, or issue resolution. The system ensures each request is assigned, processed, and resolved within agreed SLAs.
FlowInquiry automates IT service request workflows, such as software installation and access management, reducing bottlenecks caused by manual tracking. It ensures requests are assigned, processed, and resolved efficiently within SLA timelines.

By tailoring workflows to these and other scenarios, FlowInquiry empowers teams to streamline operations, meet deadlines, and deliver exceptional service to both internal and external stakeholders.

Expand Down Expand Up @@ -109,10 +110,10 @@ Set up the application environment variables by running the following script:
scripts/init_environments.sh
```

This script generates environment variables, including NEXT_PUBLIC_BACKEND_API, to establish the communication between the client and server. Example
This script generates environment variables, including BACKEND_URL, to establish the communication between the client and server. Example

```
NEXT_PUBLIC_API_BASE_URL=http://localhost:8080
BACKEND_URL=http://localhost:8080
```

We recommend running the `scripts/all.sh` script, as it streamlines the process by checking your environment settings and performing all necessary configurations, removing the need to execute multiple scripts manually.
Expand Down
2 changes: 1 addition & 1 deletion next.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ const nextConfig = {
fallback: [
{
source: "/api/:path*",
destination: `${process.env.BACKEND_API}/api/:path*`,
destination: `${process.env.BACKEND_URL}/api/:path*`,
},
],
};
Expand Down
22 changes: 11 additions & 11 deletions scripts/docker_deployment.sh
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,14 @@ docker tag ${IMAGE_NAME} ${REMOTE_REPO}:${TAG}
docker tag ${IMAGE_NAME} ${REMOTE_REPO}:latest

# Step 3: Log in to the Docker repository (Docker Hub by default)
#echo "Logging into Docker repository..."
#docker login || { echo "Login failed"; exit 1; }
#
## Step 4: Push both tags to the remote repository
#echo "Pushing Docker image to remote repository with tag '${TAG}'..."
#docker push ${REMOTE_REPO}:${TAG}
#
#echo "Pushing Docker image to remote repository with tag 'latest'..."
#docker push ${REMOTE_REPO}:latest
#
#echo "Docker image has been pushed successfully with tags '${TAG}' and 'latest'."
echo "Logging into Docker repository..."
docker login || { echo "Login failed"; exit 1; }

# Step 4: Push both tags to the remote repository
echo "Pushing Docker image to remote repository with tag '${TAG}'..."
docker push ${REMOTE_REPO}:${TAG}

echo "Pushing Docker image to remote repository with tag 'latest'..."
docker push ${REMOTE_REPO}:latest

echo "Docker image has been pushed successfully with tags '${TAG}' and 'latest'."
2 changes: 1 addition & 1 deletion scripts/init_environments.sh
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ while true; do
done

# Append to the .env.local file
echo "BACKEND_API=\"$backend_server\"" >> "$output_file"
echo "BACKEND_URL=\"$backend_server\"" >> "$output_file"


# Run npx auth and append its output to .env
Expand Down
48 changes: 20 additions & 28 deletions src/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@ import { Inter } from "next/font/google";
import AutoInitBackendApi from "@/components/init-api-backend";
import { Toaster } from "@/components/ui/toaster";
import { TooltipProvider } from "@/components/ui/tooltip";
import { setBackendApi } from "@/lib/runtime-variables";
import { BackendApiProvider } from "@/providers/backend-api-provider";
import { setBackendUrl, setBaseUrl } from "@/lib/runtime-variables";
import { ErrorProvider } from "@/providers/error-provider";
import ReactQueryProvider from "@/providers/react-query-provider";
import { ThemeProvider } from "@/providers/theme-provider";
Expand All @@ -20,23 +19,20 @@ export const metadata: Metadata = {
description: "FlowInquiry dashboard",
};

// Server-side function to fetch the BACKEND_API dynamically
async function getBackendApi(): Promise<string> {
const backendApi = process.env.BACKEND_API;
if (!backendApi) {
throw new Error("BACKEND_API is not defined in the environment");
}
return backendApi;
}

const RootLayout = async ({
children,
}: Readonly<{
children: React.ReactNode;
}>) => {
const backendApi = await getBackendApi();
const backendUrl = process.env.BACKEND_URL;
if (!backendUrl) {
throw new Error("BACKEND_URL is not defined in the environment");
}
setBackendUrl(backendUrl);

setBackendApi(backendApi);
// Base url is the mandatory field in environments except local
const baseUrl = process.env.BASE_URL;
setBaseUrl(baseUrl);

return (
<html suppressHydrationWarning={true} lang="en">
Expand All @@ -52,23 +48,19 @@ const RootLayout = async ({
<script
id="runtime-config"
dangerouslySetInnerHTML={{
__html: `window.BACKEND_API="${backendApi}";`,
__html: `window.BASE_URL="${baseUrl}";`,
}}
/>
<BackendApiProvider backendApi={backendApi}>
<ErrorProvider>
<ThemeProvider attribute="class" defaultTheme="system">
<ReactQueryProvider>
<TooltipProvider>
<AutoInitBackendApi />{" "}
{/* Initialize BACKEND_API on client */}
{children}
</TooltipProvider>
<Toaster />
</ReactQueryProvider>
</ThemeProvider>
</ErrorProvider>
</BackendApiProvider>
<ErrorProvider>
<ThemeProvider attribute="class" defaultTheme="system">
<ReactQueryProvider>
<TooltipProvider>
<AutoInitBackendApi /> {children}
</TooltipProvider>
<Toaster />
</ReactQueryProvider>
</ThemeProvider>
</ErrorProvider>
</body>
</html>
);
Expand Down
6 changes: 3 additions & 3 deletions src/components/admin-panel/user-nav.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import {
TooltipProvider,
TooltipTrigger,
} from "@/components/ui/tooltip";
import { getBackendApi } from "@/lib/runtime-variables";
import { getApiUrl, getBaseUrl } from "@/lib/runtime-variables";

export function UserNav() {
const { data: session } = useSession();
Expand All @@ -50,7 +50,7 @@ export function UserNav() {
<AvatarImage
src={
session?.user?.imageUrl
? `${getBackendApi()}/api/files/${session?.user?.imageUrl}`
? `${getApiUrl()}/api/files/${session?.user?.imageUrl}`
: undefined
}
alt="Avatar"
Expand Down Expand Up @@ -102,7 +102,7 @@ export function UserNav() {
<DropdownMenuSeparator />
<DropdownMenuItem
className="hover:cursor-pointer"
onClick={() => signOut()}
onClick={() => signOut({ redirectTo: getBaseUrl() })}
>
<LogOut className="w-4 h-4 mr-3 text-muted-foreground" />
Sign out
Expand Down
2 changes: 0 additions & 2 deletions src/components/auth/login-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ import {
FormMessage,
} from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { useBackendApi } from "@/providers/backend-api-provider";

const formSchema = z.object({
email: z
Expand All @@ -38,7 +37,6 @@ const formSchema = z.object({
});

const LoginForm = () => {
const backendApi = useBackendApi();
const router = useRouter();
const [errorMessage, setErrorMessage] = useState<string | null>(null);
const form = useForm<z.infer<typeof formSchema>>({
Expand Down
13 changes: 8 additions & 5 deletions src/components/forms/profile-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,12 @@ import { Separator } from "@/components/ui/separator";
import DefaultUserLogo from "@/components/users/user-logo";
import { useImageCropper } from "@/hooks/use-image-cropper";
import { useToast } from "@/hooks/use-toast";
import { put } from "@/lib/actions/commons.action";
import { changePassword, findUserById } from "@/lib/actions/users.action";
import { getBackendApi } from "@/lib/runtime-variables";
import {
changePassword,
findUserById,
updateUser,
} from "@/lib/actions/users.action";
import { getApiUrl } from "@/lib/runtime-variables";
import { useError } from "@/providers/error-provider";
import { UserDTOSchema } from "@/types/users";

Expand Down Expand Up @@ -89,7 +92,7 @@ export const ProfileForm = () => {
formData.append("file", selectedFile);
}

await put(`${getBackendApi()}/api/users`, formData, setError);
await updateUser(formData, setError);
toast({ description: "Save profile successfully" });
};

Expand Down Expand Up @@ -156,7 +159,7 @@ export const ProfileForm = () => {
<AvatarImage
src={
session?.user?.imageUrl
? `${getBackendApi()}/api/files/${session?.user?.imageUrl}`
? `${getApiUrl()}/api/files/${session?.user?.imageUrl}`
: ""
}
alt="@flowinquiry"
Expand Down
4 changes: 2 additions & 2 deletions src/components/init-api-backend.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@

import { useEffect } from "react";

import { initializeBackendApiOnClient } from "@/lib/runtime-variables";
import { initializeBaseUrlOnClient } from "@/lib/runtime-variables";

const AutoInitBackendApi = () => {
useEffect(() => {
initializeBackendApiOnClient();
initializeBaseUrlOnClient();
}, []);

return null; // This component is only for initialization
Expand Down
4 changes: 2 additions & 2 deletions src/components/shared/avatar-display.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import React from "react";
import DefaultTeamLogo from "@/components/teams/team-logo";
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
import DefaultUserLogo from "@/components/users/user-logo";
import { getBackendApi } from "@/lib/runtime-variables";
import { getApiUrl } from "@/lib/runtime-variables";

interface AvatarDisplayProps {
imageUrl?: string | null;
Expand All @@ -25,7 +25,7 @@ export const AvatarDisplay: React.FC<AvatarDisplayProps> = ({
return (
<Avatar className={`${size} cursor-pointer ${className}`} onClick={onClick}>
<AvatarImage
src={imageUrl ? `${getBackendApi()}/api/files/${imageUrl}` : undefined}
src={imageUrl ? `${getApiUrl()}/api/files/${imageUrl}` : undefined}
/>
<AvatarFallback>{fallbackContent}</AvatarFallback>
</Avatar>
Expand Down
20 changes: 11 additions & 9 deletions src/components/teams/team-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,13 @@ import {
TooltipTrigger,
} from "@/components/ui/tooltip";
import { useImageCropper } from "@/hooks/use-image-cropper";
import { post, put } from "@/lib/actions/commons.action";
import { findTeamById } from "@/lib/actions/teams.action";
import {
createTeam,
findTeamById,
updateTeam,
} from "@/lib/actions/teams.action";
import { obfuscate } from "@/lib/endecode";
import { getBackendApi } from "@/lib/runtime-variables";
import { getApiUrl } from "@/lib/runtime-variables";
import { validateForm } from "@/lib/validator";
import { useError } from "@/providers/error-provider";
import { TeamDTO, TeamDTOSchema } from "@/types/teams";
Expand Down Expand Up @@ -104,13 +107,12 @@ export const TeamForm = ({ teamId }: { teamId: number | undefined }) => {
if (formValues.id) {
// Edit mode
redirectTeamId = formValues.id;
await put(`${getBackendApi()}/api/teams`, formData);
await updateTeam(formData, setError);
} else {
// Create mode
await post<FormData, TeamDTO>(
`${getBackendApi()}/api/teams`,
formData,
).then((data) => (redirectTeamId = data.id));
await createTeam(formData, setError).then(
(data) => (redirectTeamId = data.id),
);
}

router.push(`/portal/teams/${obfuscate(redirectTeamId)}/dashboard`);
Expand Down Expand Up @@ -173,7 +175,7 @@ export const TeamForm = ({ teamId }: { teamId: number | undefined }) => {
<AvatarImage
src={
team?.logoUrl
? `${getBackendApi()}/api/files/${team.logoUrl}`
? `${getApiUrl()}/api/files/${team.logoUrl}`
: undefined
}
alt="@flowinquiry"
Expand Down
4 changes: 2 additions & 2 deletions src/components/teams/team-list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ import { useDebouncedCallback } from "@/hooks/use-debounced-callback";
import { usePagePermission } from "@/hooks/use-page-permission";
import { deleteTeams, searchTeams } from "@/lib/actions/teams.action";
import { obfuscate } from "@/lib/endecode";
import { getBackendApi } from "@/lib/runtime-variables";
import { getApiUrl } from "@/lib/runtime-variables";
import { cn } from "@/lib/utils";
import { useError } from "@/providers/error-provider";
import { Filter, QueryDTO } from "@/types/query";
Expand Down Expand Up @@ -206,7 +206,7 @@ export const TeamList = () => {
<AvatarImage
src={
team.logoUrl
? `${getBackendApi()}/api/files/${team.logoUrl}`
? `${getApiUrl()}/api/files/${team.logoUrl}`
: undefined
}
alt="@flowinquiry"
Expand Down
4 changes: 2 additions & 2 deletions src/components/teams/team-users.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ import {
findMembersByTeamId,
} from "@/lib/actions/teams.action";
import { obfuscate } from "@/lib/endecode";
import { getBackendApi } from "@/lib/runtime-variables";
import { getApiUrl } from "@/lib/runtime-variables";
import { BreadcrumbProvider } from "@/providers/breadcrumb-provider";
import { useError } from "@/providers/error-provider";
import { useTeam } from "@/providers/team-provider";
Expand Down Expand Up @@ -170,7 +170,7 @@ const TeamUsersView = () => {
<AvatarImage
src={
user?.imageUrl
? `${getBackendApi()}/api/files/${user.imageUrl}`
? `${getApiUrl()}/api/files/${user.imageUrl}`
: undefined
}
alt={`${user.firstName} ${user.lastName}`}
Expand Down
2 changes: 1 addition & 1 deletion src/global.d.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
declare global {
interface Window {
BACKEND_API?: string; // Add BACKEND_API as an optional property
BASE_URL?: string; // Add BACKEND_URL as an optional property
}
}

Expand Down
7 changes: 4 additions & 3 deletions src/lib/actions/commons.action.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { auth } from "@/auth";
import { getAccessToken } from "@/lib/access-token-manager";
import { handleError, HttpError } from "@/lib/errors";
import { getBackendApi } from "@/lib/runtime-variables";
import { getApiUrl } from "@/lib/runtime-variables";
import { PageableResult } from "@/types/commons";
import {
createQueryParams,
Expand Down Expand Up @@ -42,9 +42,10 @@ export const fetchData = async <TData, TResponse>(
options.body = JSON.stringify(data);
}

const BACKEND_API = getBackendApi();
const apiUrl = getApiUrl();

try {
const response = await fetch(`${BACKEND_API}${url}`, options);
const response = await fetch(`${apiUrl}${url}`, options);

if (response.ok) {
const contentType = response.headers.get("content-type");
Expand Down
Loading

0 comments on commit a36698c

Please sign in to comment.