Skip to content

Commit

Permalink
Added in the skelton for the api handler.
Browse files Browse the repository at this point in the history
- This is a wrapper around Axios for get/post commands.
- Created a series of hooks to handle how we process errors and success.
- Added this to the container
- Created a context wrapper.
  • Loading branch information
AdamKyle committed Dec 29, 2024
1 parent 7a2a7a7 commit 75dc4ed
Show file tree
Hide file tree
Showing 26 changed files with 615 additions and 327 deletions.
22 changes: 11 additions & 11 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
"animate.css": "^4.1.1",
"axios": "^1.6.8",
"cross-env": "^7.0.3",
"date-fns": "3.6.0",
"date-fns": "4.1.0",
"depcheck": "^1.4.7",
"eslint": "^9.17.0",
"eslint-config-prettier": "^9.1.0",
Expand All @@ -37,29 +37,29 @@
"lodash": "^4.17.21",
"popper.js": "^1.14.3",
"prettier": "^3.4.2",
"react": "^18.0.*",
"react-dom": "^18.0.*",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"tailwindcss": "^3.4.6",
"tailwindcss-dir": "^4.0.0",
"ts-loader": "9.5.1",
"typescript": "^5.5.3",
"typescript-eslint": "^8.12.0",
"vite": "^5.1.3"
"vite": "^6.0.6"
},
"dependencies": {
"@headlessui/react": "2.1.2",
"@headlessui/react": "2.2.0",
"@types/lodash": "^4.14.180",
"@types/node": "^20.12.7",
"@types/react": "^18.0.*",
"@types/react-dom": "^18.0.*",
"@types/node": "^22.10.2",
"@types/react": "^19.0.2",
"@types/react-dom": "^19.0.2",
"@types/react-resizable": "^3.0.7",
"clsx": "^2.1.1",
"framer-motion": "^11.11.11",
"laravel-echo": "^1.16.1",
"postcss-custom-properties": "^13.3.8",
"postcss-custom-properties": "^14.0.4",
"postcss-import": "^16.1.0",
"postcss-nested": "^6.0.1",
"postcss-nesting": "^12.1.2",
"postcss-nested": "^7.0.2",
"postcss-nesting": "^13.0.1",
"pusher-js": "^8.4.0-rc2",
"reflect-metadata": "^0.2.2",
"rpg-awesome": "^0.2.0",
Expand Down
5 changes: 5 additions & 0 deletions resources/js/axios/api-handler-context.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { createContext } from 'react';

import AxiosDefinition from './definitions/axios-definition';

export const ApiHandlerContext = createContext<AxiosDefinition | null>(null);
11 changes: 11 additions & 0 deletions resources/js/axios/axios-service-container.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { ModularContainerDefinition } from 'configuration/deffinitions/modular-container-definition';

import Axios from './axios';
import AxiosDefinition from './definitions/axios-definition';
import CoreContainerDefinition from '../service-container/deffinitions/core-container-definition';

export const axiosServiceContainer: ModularContainerDefinition = (
container: CoreContainerDefinition
) => {
container.register<AxiosDefinition>('ApiHandler', new Axios());
};
19 changes: 19 additions & 0 deletions resources/js/axios/axios.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import axios, { AxiosRequestConfig } from 'axios';

import AxiosDefinition from './definitions/axios-definition';

export default class Axios implements AxiosDefinition {
async get<T>(url: string, config?: AxiosRequestConfig): Promise<T> {
const response = await axios.get<T>(url, config);
return response.data;
}

async post<T, D>(
url: string,
data: D,
config?: AxiosRequestConfig
): Promise<T> {
const response = await axios.post<T>(url, data, config);
return response.data;
}
}
19 changes: 19 additions & 0 deletions resources/js/axios/components/api-handler-provider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import React, { ReactNode } from 'react';

import { ApiHandlerContext } from '../api-handler-context';
import ApiHandlerProviderProps from './types/api-handler-provider-props';
import AxiosDefinition from '../definitions/axios-definition';

import { serviceContainer } from 'service-container/core-container';

export const ApiHandlerProvider = (
props: ApiHandlerProviderProps
): ReactNode => {
const apiHandler = serviceContainer().fetch<AxiosDefinition>('ApiHandler');

return (
<ApiHandlerContext.Provider value={apiHandler}>
{props.children}
</ApiHandlerContext.Provider>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import React from 'react';

export default interface ApiHandlerProviderProps {
children: React.ReactNode;
}
18 changes: 18 additions & 0 deletions resources/js/axios/definitions/axios-definition.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
export default interface AxiosDefinition {
/**
* Handles get requests.
*
* @param url
* @param config
*/
get<T, C>(url: string, config?: Record<string, C>): Promise<T>;

/**
* handles post requests.
*
* @param url
* @param data
* @param config
*/
post<T, C, D>(url: string, data: D, config?: Record<string, C>): Promise<T>;
}
3 changes: 3 additions & 0 deletions resources/js/axios/definitions/use-axios-error-definition.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default interface AxiosErrorMessageDefinition {
errorMessage: string;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default interface AxiosErrorMessageDefinition {
onError: (message: string) => void;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default interface UseAxiosSuccessDefinition<T> {
data: T | null;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default interface UseAxiosSuccessHandlerDefinition<T> {
onSuccess: (data: T) => void;
}
4 changes: 4 additions & 0 deletions resources/js/axios/event-types/axios-events-types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export enum AxiosEventTypes {
SUCCESS = 'success',
ERROR = 'error',
}
14 changes: 14 additions & 0 deletions resources/js/axios/hooks/use-api-handler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { useContext } from 'react';

import { ApiHandlerContext } from '../api-handler-context';
import AxiosDefinition from '../definitions/axios-definition';

export const useApiHandler = (): AxiosDefinition => {
const context = useContext(ApiHandlerContext);

if (!context) {
throw new Error('useApiHandler must be used within an ApiHandlerContext');
}

return context;
};
26 changes: 26 additions & 0 deletions resources/js/axios/hooks/use-axios-error-message-handler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { useEventSystem } from 'event-system/hooks/use-event-system';

import AxiosErrorMessageDefinition from '../definitions/use-axios-error-message-handler-definition';
import { AxiosEventTypes } from '../event-types/axios-events-types';

export const useAxiosErrorMessageHandler = (): AxiosErrorMessageDefinition => {
const eventSystem = useEventSystem();

const manageAxiosErrorEmitter = eventSystem.isEventRegistered(
AxiosEventTypes.ERROR
)
? eventSystem.getEventEmitter<{ [key: string]: string }>(
AxiosEventTypes.ERROR
)
: eventSystem.registerEvent<{ [key: string]: string }>(
AxiosEventTypes.ERROR
);

const onError = (message: string) => {
manageAxiosErrorEmitter.emit(AxiosEventTypes.ERROR, message);
};

return {
onError,
};
};
33 changes: 33 additions & 0 deletions resources/js/axios/hooks/use-axios-error.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { useEventSystem } from 'event-system/hooks/use-event-system';
import { useEffect, useState } from 'react';

import AxiosErrorMessageDefinition from '../definitions/use-axios-error-definition';
import { AxiosEventTypes } from '../event-types/axios-events-types';
import UseAxiosErrorState from '../types/use-axios-error-state';

export const useAxiosError = (): AxiosErrorMessageDefinition => {
const eventSystem = useEventSystem();

const [errorMessage, setErrorMessage] =
useState<UseAxiosErrorState['errorMessage']>('');

const manageAxiosErrorEmitter = eventSystem.getEventEmitter<{
[key: string]: string;
}>(AxiosEventTypes.ERROR);

useEffect(() => {
const updateMessage = (message: string) => {
setErrorMessage(message);
};

manageAxiosErrorEmitter.on(AxiosEventTypes.ERROR, updateMessage);

return () => {
manageAxiosErrorEmitter.off(AxiosEventTypes.ERROR, updateMessage);
};
}, [manageAxiosErrorEmitter]);

return {
errorMessage,
};
};
24 changes: 24 additions & 0 deletions resources/js/axios/hooks/use-axios-success-handler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { useEventSystem } from 'event-system/hooks/use-event-system';

import UseAxiosSuccessHandlerDefinition from '../definitions/use-axios-success-handler-definition';
import { AxiosEventTypes } from '../event-types/axios-events-types';

export const useAxiosSuccessHandler = <
T,
>(): UseAxiosSuccessHandlerDefinition<T> => {
const eventSystem = useEventSystem();

const manageAxiosSuccessEmitter = eventSystem.isEventRegistered(
AxiosEventTypes.SUCCESS
)
? eventSystem.getEventEmitter<{ [key: string]: T }>(AxiosEventTypes.SUCCESS)
: eventSystem.registerEvent<{ [key: string]: T }>(AxiosEventTypes.SUCCESS);

const onSuccess = (data: T): void => {
manageAxiosSuccessEmitter.emit(AxiosEventTypes.SUCCESS, data);
};

return {
onSuccess,
};
};
32 changes: 32 additions & 0 deletions resources/js/axios/hooks/use-axios-success.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { useEventSystem } from 'event-system/hooks/use-event-system';
import { useEffect, useState } from 'react';

import UseAxiosSuccessDefinition from '../definitions/use-axios-success-definition';
import { AxiosEventTypes } from '../event-types/axios-events-types';
import UseAxiosSuccessState from '../types/use-axios-success-state';

export const useAxiosError = <T>(): UseAxiosSuccessDefinition<T> => {
const eventSystem = useEventSystem();

const [data, setData] = useState<UseAxiosSuccessState<T>['data']>(null);

const manageAxiosErrorEmitter = eventSystem.getEventEmitter<{
[key: string]: T;
}>(AxiosEventTypes.SUCCESS);

useEffect(() => {
const updateData = (data: T) => {
setData(data);
};

manageAxiosErrorEmitter.on(AxiosEventTypes.SUCCESS, updateData);

return () => {
manageAxiosErrorEmitter.off(AxiosEventTypes.SUCCESS, updateData);
};
}, [manageAxiosErrorEmitter]);

return {
data,
};
};
3 changes: 3 additions & 0 deletions resources/js/axios/types/use-axios-error-state.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default interface UseAxiosErrorState {
errorMessage: string;
}
3 changes: 3 additions & 0 deletions resources/js/axios/types/use-axios-success-state.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default interface UseAxiosSuccessState<T> {
data: T | null;
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import CoreContainerDefinition from '../../service-container/deffinitions/core-container-definition';

export type ModularContainerDeffintion = (
export type ModularContainerDefinition = (
container: CoreContainerDefinition
) => void;
6 changes: 4 additions & 2 deletions resources/js/configuration/modular-container.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { eventServiceContainer } from 'event-system/event-service-container';

import { ModularContainerDeffintion } from './deffinitions/modular-container-deffintion';
import { ModularContainerDefinition } from './deffinitions/modular-container-definition';
import { axiosServiceContainer } from '../axios/axios-service-container';

/**
* Register service containers here.
*/
export const serviceContainers: ModularContainerDeffintion[] = [
export const serviceContainers: ModularContainerDefinition[] = [
eventServiceContainer,
axiosServiceContainer,
];
5 changes: 3 additions & 2 deletions resources/js/event-system/event-service-container.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { ModularContainerDefinition } from 'configuration/deffinitions/modular-container-definition';

import EventSystemDefinition from './deffintions/event-system-definition';
import EventSystem from './event-system';
import { ModularContainerDeffintion } from '../configuration/deffinitions/modular-container-deffintion';
import CoreContainerDefinition from '../service-container/deffinitions/core-container-definition';

export const eventServiceContainer: ModularContainerDeffintion = (
export const eventServiceContainer: ModularContainerDefinition = (
container: CoreContainerDefinition
) => {
container.registerSingleton<EventSystemDefinition>(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,25 @@ const EquippedSlot = (props: EquippedSlotProps): ReactNode => {
const altText = defaultPositionImageAlt[position];

return (
<div
className={`w-16 h-16 text-white flex items-center justify-center border border-gray-600 rounded`}
<button
className={
'w-16 h-16 text-white flex items-center justify-center border border-gray-600 rounded focus:outline-none ' +
'focus:ring-2 focus:ring-offset-2 focus:ring-gray-600 ' +
'hover:bg-gray-200 dark:hover:bg-gray-700 dark:focus:ring-gray-500'
}
onClick={() => {}}
onMouseOver={(e: React.MouseEvent<HTMLDivElement>) => {
e.currentTarget.title = `${positionName}: ${itemName}`;
onMouseOver={(e: React.MouseEvent<HTMLButtonElement>) => {
e.currentTarget.setAttribute('title', `${positionName}: ${itemName}`);
}}
onFocus={(e: React.FocusEvent<HTMLButtonElement>) => {
e.currentTarget.setAttribute('title', `${positionName}: ${itemName}`);
}}
aria-label={`${positionName}: ${itemName}`}
aria-labelledby={positionName}
role="button"
>
<img src={path} width={64} alt={altText} />
</div>
</button>
);
};

Expand Down
Loading

0 comments on commit 75dc4ed

Please sign in to comment.