Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .hintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"connector": {
"name": "local"
},
"hints": {
"no-inline-styles": "off"
}
}
9 changes: 9 additions & 0 deletions .vscode/extensions.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"recommendations": [
"dbaeumer.vscode-eslint",
"esbenp.prettier-vscode"
],
"unwantedRecommendations": [
"ms-edgedevtools.vscode-edge-devtools"
]
}
12 changes: 12 additions & 0 deletions frontend/.eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,18 @@
"no-warning-comments": [
"error",
{ "terms": ["todo", "fixme"], "location": "anywhere" }
],
// Allow style prop for CSS variables, but keep dangerouslySetInnerHTML forbidden
"react/forbid-dom-props": [
"error",
{
"forbid": [
{
"propName": "dangerouslySetInnerHTML",
"message": "Use safe alternatives instead of dangerouslySetInnerHTML"
}
]
}
]
}
}
8 changes: 8 additions & 0 deletions frontend/.hintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"connector": {
"name": "local"
},
"hints": {
"no-inline-styles": "off"
}
}
11 changes: 11 additions & 0 deletions frontend/.vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"typescript.tsdk": "node_modules/typescript/lib",
"typescript.enablePromptUseWorkspaceTsdk": true,
"css.lint.validProperties": [
"--sidebar-width",
"--sidebar-width-icon"
],
"css.validate": false,
"webhint.enable": false,
"edge-devtools-network.enable": false
}
Empty file added frontend/TYPESCRIPT_SETUP.md
Empty file.
4 changes: 2 additions & 2 deletions frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions frontend/package.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"name": "PictoPy",
"name": "pictopy-frontend",
"private": true,
"version": "1.0.0",
"type": "module",
Expand All @@ -14,7 +14,8 @@
"lint:check": "eslint --max-warnings 0 --config .eslintrc.json .",
"lint:fix": "eslint --max-warnings 0 --config .eslintrc.json . --fix",
"format:fix": "prettier --write \"**/*.{ts,tsx,json}\"",
"format:check": "prettier --check \"**/*.{ts,tsx,json}\""
"format:check": "prettier --check \"**/*.{ts,tsx,json}\"",
"type-check": "tsc --noEmit"
},
"lint-staged": {
"*.{js,jsx,ts,tsx}": [
Expand Down
154 changes: 154 additions & 0 deletions frontend/src/api/api-functions/albums.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
import { APIResponse } from '@/types/API';
import { albumsEndpoints } from '../apiEndpoints';
import { apiClient } from '../axiosConfig';

export interface Album {
album_id: string;
album_name: string;
description: string;
is_hidden: boolean;
}

export interface CreateAlbumRequest {
name: string;
description?: string;
is_hidden: boolean;
password?: string;
}

export interface UpdateAlbumRequest {
name: string;
description?: string;
is_hidden: boolean;
current_password?: string;
password?: string;
}

export interface GetAlbumImagesRequest {
password?: string;
}

export interface ImageIdsRequest {
image_ids: string[];
}

export interface GetAlbumsResponse extends APIResponse {
albums: Album[];
}

export interface CreateAlbumResponse extends APIResponse {
album_id: string;
}

export interface GetAlbumResponse extends APIResponse {
data: Album;
}

export interface GetAlbumImagesResponse extends APIResponse {
image_ids: string[];
}

// Get all albums
export const fetchAllAlbums = async (
showHidden = false,
): Promise<GetAlbumsResponse> => {
const response = await apiClient.get<GetAlbumsResponse>(
`${albumsEndpoints.getAllAlbums}?show_hidden=${showHidden}`,
);
return response.data;
};

// Create a new album
export const createAlbum = async (
albumData: CreateAlbumRequest,
): Promise<CreateAlbumResponse> => {
const response = await apiClient.post<CreateAlbumResponse>(
albumsEndpoints.createAlbum,
albumData,
);
return response.data;
};

// Get specific album details
export const fetchAlbum = async (
albumId: string,
): Promise<GetAlbumResponse> => {
const response = await apiClient.get<GetAlbumResponse>(
albumsEndpoints.getAlbum(albumId),
);
return response.data;
};

// Update album
export const updateAlbum = async (
albumId: string,
albumData: UpdateAlbumRequest,
): Promise<APIResponse> => {
const response = await apiClient.put<APIResponse>(
albumsEndpoints.updateAlbum(albumId),
albumData,
);
return response.data;
};

// Delete album
export const deleteAlbum = async (albumId: string): Promise<APIResponse> => {
const response = await apiClient.delete<APIResponse>(
albumsEndpoints.deleteAlbum(albumId),
);
return response.data;
};

// Get album images
export const fetchAlbumImages = async (
albumId: string,
password?: string,
): Promise<GetAlbumImagesResponse> => {
const requestBody: GetAlbumImagesRequest = {};
if (password) {
requestBody.password = password;
}

const response = await apiClient.post<GetAlbumImagesResponse>(
albumsEndpoints.getAlbumImages(albumId),
requestBody,
);
return response.data;
};

// Add images to album
export const addImagesToAlbum = async (
albumId: string,
imageIds: string[],
): Promise<APIResponse> => {
const requestBody: ImageIdsRequest = { image_ids: imageIds };
const response = await apiClient.post<APIResponse>(
albumsEndpoints.addImagesToAlbum(albumId),
requestBody,
);
return response.data;
};

// Remove image from album
export const removeImageFromAlbum = async (
albumId: string,
imageId: string,
): Promise<APIResponse> => {
const response = await apiClient.delete<APIResponse>(
albumsEndpoints.removeImageFromAlbum(albumId, imageId),
);
return response.data;
};

// Remove multiple images from album
export const removeImagesFromAlbum = async (
albumId: string,
imageIds: string[],
): Promise<APIResponse> => {
const requestBody: ImageIdsRequest = { image_ids: imageIds };
const response = await apiClient.delete<APIResponse>(
albumsEndpoints.removeImagesFromAlbum(albumId),
{ data: requestBody },
);
return response.data;
};
5 changes: 3 additions & 2 deletions frontend/src/api/api-functions/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Export all API functions
export * from './albums';
export * from './face_clusters';
export * from './images';
export * from './folders';
export * from './user_preferences';
export * from './health';
export * from './images';
export * from './user_preferences';
13 changes: 13 additions & 0 deletions frontend/src/api/apiEndpoints.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,19 @@ export const userPreferencesEndpoints = {
updateUserPreferences: '/user-preferences/',
};

export const albumsEndpoints = {
getAllAlbums: '/albums/',
createAlbum: '/albums/',
getAlbum: (albumId: string) => `/albums/${albumId}`,
updateAlbum: (albumId: string) => `/albums/${albumId}`,
deleteAlbum: (albumId: string) => `/albums/${albumId}`,
getAlbumImages: (albumId: string) => `/albums/${albumId}/images/get`,
addImagesToAlbum: (albumId: string) => `/albums/${albumId}/images`,
removeImageFromAlbum: (albumId: string, imageId: string) =>
`/albums/${albumId}/images/${imageId}`,
removeImagesFromAlbum: (albumId: string) => `/albums/${albumId}/images`,
};

export const healthEndpoints = {
healthCheck: '/health',
};
12 changes: 7 additions & 5 deletions frontend/src/app/store.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { configureStore } from '@reduxjs/toolkit';
import albumReducer from '@/features/albumSlice';
import faceClustersReducer from '@/features/faceClustersSlice';
import folderReducer from '@/features/folderSlice';
import imageReducer from '@/features/imageSlice';
import infoDialogReducer from '@/features/infoDialogSlice';
import loaderReducer from '@/features/loaderSlice';
import onboardingReducer from '@/features/onboardingSlice';
import searchReducer from '@/features/searchSlice';
import imageReducer from '@/features/imageSlice';
import faceClustersReducer from '@/features/faceClustersSlice';
import infoDialogReducer from '@/features/infoDialogSlice';
import folderReducer from '@/features/folderSlice';
import { configureStore } from '@reduxjs/toolkit';

export const store = configureStore({
reducer: {
Expand All @@ -16,6 +17,7 @@ export const store = configureStore({
infoDialog: infoDialogReducer,
folders: folderReducer,
search: searchReducer,
albums: albumReducer,
},
});
// Infer the `RootState` and `AppDispatch` types from the store itself
Expand Down
Loading
Loading