Skip to content

Commit

Permalink
refactor: rabbit suggestions
Browse files Browse the repository at this point in the history
  • Loading branch information
nicotsx committed Dec 7, 2024
1 parent 984452c commit 72d1e02
Show file tree
Hide file tree
Showing 12 changed files with 86 additions and 52 deletions.
23 changes: 20 additions & 3 deletions packages/backend/src/modules/app-stores/app-store.repository.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { DatabaseService } from '@/core/database/database.service';
import { app, appStore } from '@/core/database/drizzle/schema';
import type { AppStore, NewAppStore } from '@/core/database/drizzle/types';
import type { NewAppStore } from '@/core/database/drizzle/types';
import { Injectable } from '@nestjs/common';
import { and, asc, count, eq } from 'drizzle-orm';
import { ReposHelpers } from './repos.helpers';
Expand Down Expand Up @@ -29,7 +29,13 @@ export class AppStoreRepository {
.insert(appStore)
.values({ ...data, hash })
.returning();
return newAppStore[0] as AppStore;

const insertedAppStore = newAppStore[0];
if (!insertedAppStore) {
throw new Error('Failed to create new app store.');
}

return insertedAppStore;
}

public async getEnabledAppStores() {
Expand All @@ -53,7 +59,18 @@ export class AppStoreRepository {
}

public async updateAppStore(id: number, data: Omit<NewAppStore, 'hash' | 'id' | 'url'>) {
return this.databaseService.db.update(appStore).set({ name: data.name, enabled: data.enabled, deleted: false }).where(eq(appStore.id, id));
const update = await this.databaseService.db
.update(appStore)
.set({ name: data.name, enabled: data.enabled, deleted: false })
.where(eq(appStore.id, id))
.returning();
const store = update[0];

if (!store) {
throw new Error('Failed to update app store.');
}

return store;
}

public async enableAppStore(id: number) {
Expand Down
18 changes: 14 additions & 4 deletions packages/backend/src/modules/app-stores/app-store.service.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { TranslatableError } from '@/common/error/translatable-error';
import { ConfigurationService } from '@/core/config/configuration.service';
import { LoggerService } from '@/core/logger/logger.service';
import { HttpStatus, Injectable } from '@nestjs/common';
import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
import type { UpdateAppStoreBodyDto } from '../marketplace/dto/marketplace.dto';
import { RepoEventsQueue } from '../queue/entities/repo-events';
import { AppStoreRepository } from './app-store.repository';
Expand Down Expand Up @@ -100,7 +100,12 @@ export class AppStoreService {
* @param body The new data to update the app store with
*/
public async updateAppStore(id: string, body: UpdateAppStoreBodyDto) {
return this.appStoreRepository.updateAppStore(Number(id), body);
const numericId = Number(id);
if (Number.isNaN(numericId)) {
throw new HttpException(`Invalid ID ${id}`, HttpStatus.BAD_REQUEST);
}

return this.appStoreRepository.updateAppStore(numericId, body);
}

/**
Expand All @@ -115,13 +120,18 @@ export class AppStoreService {
throw new TranslatableError('APP_STORE_DELETE_ERROR_LAST_STORE', {}, HttpStatus.BAD_REQUEST);
}

const count = await this.appStoreRepository.getAppCountForStore(Number(id));
const numericId = Number(id);
if (Number.isNaN(numericId)) {
throw new HttpException(`Invalid ID ${id}`, HttpStatus.BAD_REQUEST);
}

const count = await this.appStoreRepository.getAppCountForStore(numericId);

if (count && count.count > 0) {
throw new TranslatableError('APP_STORE_DELETE_ERROR_APPS_EXIST', {}, HttpStatus.BAD_REQUEST);
}

await this.appStoreRepository.deleteAppStore(Number(id));
await this.appStoreRepository.deleteAppStore(numericId);
await this.repoHelpers.deleteRepo(id);

return { success: true };
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,10 @@ export class MarketplaceController {
@Post('create')
@UseGuards(AuthGuard)
async createAppStore(@Body() body: CreateAppStoreBodyDto) {
await this.appStoreService.createAppStore(body);
const appStore = await this.appStoreService.createAppStore(body);
await this.marketplaceService.initialize();

return { appStore };
}

@Get('all')
Expand All @@ -91,12 +93,16 @@ export class MarketplaceController {
async updateAppStore(@Param('id') id: string, @Body() body: UpdateAppStoreBodyDto) {
await this.appStoreService.updateAppStore(id, body);
await this.marketplaceService.initialize();

return { success: true };
}

@Delete(':id')
@UseGuards(AuthGuard)
async deleteAppStore(@Param('id') id: string) {
await this.appStoreService.deleteAppStore(id);
await this.marketplaceService.initialize();

return { success: true };
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,10 @@ export class MarketplaceService {
let filteredApps = await this.getAvailableApps();

if (storeId) {
filteredApps = filteredApps.filter((app) => app.id.endsWith(`_${storeId}`));
filteredApps = filteredApps.filter((app) => {
const { storeId: appStoreId } = extractAppId(app.id);
return appStoreId === storeId.toString();
});
}

if (category) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,17 @@ import { Input } from '@/components/ui/Input';
import { useAppStoreState } from '@/stores/app-store';
import clsx from 'clsx';
import type React from 'react';
import { useState } from 'react';
import { useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import './app-store-layout-actions.css';
import { getEnabledAppStoresOptions } from '@/api-client/@tanstack/react-query.gen';
import { useSuspenseQuery } from '@tanstack/react-query';
import { useSearchParams } from 'react-router';
import { CategorySelector } from '../category-selector/category-selector';
import { StoreSelector } from '../store-selector/store-selector';

export const AppStoreLayoutActions = () => {
const { setCategory, category, storeId, setStoreId, search: initialSearch, setSearch } = useAppStoreState();
const { setCategory, category, setStoreId, search: initialSearch, setSearch } = useAppStoreState();
const [search, setLocalSearch] = useState(initialSearch);
const { t } = useTranslation();

Expand All @@ -24,6 +25,24 @@ export const AppStoreLayoutActions = () => {
setSearch(e.target.value);
};

const [params, setParams] = useSearchParams();
const selectedStore = params.get('store') ?? undefined;

useEffect(() => {
if (selectedStore) {
setStoreId(Number.parseInt(selectedStore));
}
}, [selectedStore, setStoreId]);

const onSelectStore = (value?: string) => {
if (value) {
setParams({ store: value });
} else {
setParams({});
}
setStoreId(value ? Number.parseInt(value) : undefined);
};

return (
<div className="d-flex align-items-stretch align-items-md-center flex-column flex-md-row justify-content-end">
<Input
Expand All @@ -34,10 +53,10 @@ export const AppStoreLayoutActions = () => {
/>
{data.appStores.length > 1 && (
<StoreSelector
initialValue={selectedStore}
stores={data.appStores}
initialValue={storeId?.toString()}
className={clsx('flex-fill mt-2 mt-md-0 search-input me-2')}
onSelect={(value) => setStoreId(value ? Number.parseInt(value) : undefined)}
onSelect={onSelectStore}
/>
)}
<CategorySelector initialValue={category} className={clsx('flex-fill mt-2 mt-md-0 search-input')} onSelect={setCategory} />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ interface Props {

export const CategorySelector = ({ onSelect, className, initialValue }: Props) => {
const { t } = useTranslation();
const [key, setKey] = useState(new Date().getTime().toString());
const [resetCounter, setResetCounter] = useState(0);

const options = iconForCategory.map((category) => ({
value: category.id,
Expand All @@ -30,11 +30,11 @@ export const CategorySelector = ({ onSelect, className, initialValue }: Props) =
const handleReset = () => {
setValue(undefined);
onSelect(undefined);
setKey(new Date().getTime().toString());
setResetCounter((prev) => prev + 1);
};

return (
<Select key={key} value={value} onValueChange={(o: AppCategory) => handleChange(o)}>
<Select key={resetCounter.toString()} value={value} onValueChange={(o: AppCategory) => handleChange(o)}>
<SelectTrigger value={value} onClear={handleReset} className={className}>
<SelectValue placeholder={t('APP_STORE_CHOOSE_CATEGORY')} />
</SelectTrigger>
Expand Down
9 changes: 4 additions & 5 deletions packages/frontend/src/components/ui/Select/Select.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,15 +52,14 @@ const SelectTrigger = React.forwardRef<
{value && (
<button
type="button"
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
aria-label="Clear selection"
onClick={(_) => {
onClear?.();
}}
className="btn btn-link position-absolute top-50 end-0 translate-middle-y pe-3"
className="btn btn-link position-absolute top-50 end-0 translate-middle-y"
style={{ marginRight: '1rem' }}
>
<IconX size={14} />
<IconX size={14} aria-hidden />
</button>
)}
</div>
Expand Down
2 changes: 1 addition & 1 deletion packages/frontend/src/modules/app/pages/app-store-page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export const AppStorePage = () => {
});

if (params.storeId) {
return <Navigate to="/app-store" />;
return <Navigate to={`/app-store?store=${params.storeId}`} />;
}

if (isLoading) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ export const AddAppStoreDialog = () => {

type FormValues = z.infer<typeof schema>;

const { register, reset, handleSubmit, formState } = useForm<FormValues>({
resolver: zodResolver(schema),
});

const createAppStore = useMutation({
...createAppStoreMutation(),
onError: (e: TranslatableError) => {
Expand All @@ -31,13 +35,10 @@ export const AddAppStoreDialog = () => {
onSuccess: () => {
toast.success(t('APP_STORE_ADD_SUCCESS'));
addAppStoreDisclosure.close();
reset();
},
});

const { register, handleSubmit, formState } = useForm<FormValues>({
resolver: zodResolver(schema),
});

const onSubmit = (values: FormValues) => {
createAppStore.mutate({ body: values });
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ export const AppStoresTable = ({ appStores }: Props) => {
<EnabledBadge enabled={appStore.enabled} />
</TableCell>
<TableCell>
<a href={appStore.url} target="_blank" rel="noreferrer">
<a href={appStore.url} target="_blank" rel="noreferrer noopener nofollow">
{appStore.url}
</a>
</TableCell>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,14 @@ export const DeleteAppStoreDialog = ({ appStore }: Props) => {
toast.error(t(e.message, e.intlParams));
},
onSuccess: () => {
toast.success('APP_STORE_DELETE_SUCCESS');
toast.success(t('APP_STORE_DELETE_SUCCESS'));
deleteAppStoreDisclosure.close();
},
});

return (
<div>
<Button size="sm" intent="danger" variant="ghost" disabled={length === 1} onClick={() => deleteAppStoreDisclosure.open()}>
<Button size="sm" intent="danger" variant="ghost" disabled={deleteAppStore.isPending} onClick={() => deleteAppStoreDisclosure.open()}>
{t('APP_STORE_TABLE_DELETE')}
</Button>
<Dialog open={deleteAppStoreDisclosure.isOpen} onOpenChange={deleteAppStoreDisclosure.toggle}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import { useId } from 'react';
import { Controller, useForm } from 'react-hook-form';
import toast from 'react-hot-toast';
import { useTranslation } from 'react-i18next';
import validator from 'validator';
import { z } from 'zod';

type Props = {
Expand All @@ -37,37 +36,17 @@ export const EditAppStoreDialog = ({ appStore }: Props) => {
},
onSuccess: () => {
editAppStoreDisclosure.close();
toast.success('APP_STORE_EDIT_DIALOG_SUCCESS');
toast.success(t('APP_STORE_EDIT_DIALOG_SUCCESS'));
},
});

const validateFields = (values: FormValues) => {
const errors: { [k in keyof FormValues]?: string } = {};

if (values.name && !validator.isLength(values.name, { max: 16 })) {
errors.name = t('APP_INSTALL_FORM_ERROR_BETWEEN_LENGTH', { label: 'Name', min: 1, max: 16 });
}

return errors;
};

const { register, control, handleSubmit, setError, formState } = useForm<FormValues>({
resolver: zodResolver(schema),
values: appStore,
});

const validate = (values: FormValues) => {
const errors = validateFields(values);

for (const [field, message] of Object.entries(errors)) {
if (message) {
setError(field as keyof FormValues, { message });
}
}

if (Object.keys(errors).length === 0) {
editAppStore.mutate({ path: { id: appStore.id.toString() }, body: values });
}
editAppStore.mutate({ path: { id: appStore.id.toString() }, body: values });
};

const formId = useId();
Expand Down

0 comments on commit 72d1e02

Please sign in to comment.