first commit
Some checks failed
Build / run (push) Has been cancelled

This commit is contained in:
maher
2025-10-29 11:42:25 +01:00
commit 703f50a09d
4595 changed files with 385164 additions and 0 deletions

View File

@@ -0,0 +1,101 @@
import {Fragment, useRef} from 'react';
import {Trans} from '@common/i18n/trans';
import {DialogTrigger} from '@common/ui/overlays/dialog/dialog-trigger';
import {AppearanceButton} from '@common/admin/appearance/appearance-button';
import {AceDialog} from '@common/ace-editor/ace-dialog';
import mergedAppearanceConfig from '@common/admin/appearance/config/merged-appearance-config';
import {SeoSettingsSectionConfig} from '@common/admin/appearance/types/appearance-editor-config';
import {MessageDescriptor} from '@common/i18n/message-descriptor';
import {useSeoTags} from '@common/admin/appearance/sections/seo/use-seo-tags';
import {useUpdateSeoTags} from '@common/admin/appearance/sections/seo/use-update-seo-tags';
import {useDialogContext} from '@common/ui/overlays/dialog/dialog-context';
import {FullPageLoader} from '@common/ui/progress/full-page-loader';
import {Button} from '@common/ui/buttons/button';
import type ReactAce from 'react-ace';
const pages =
(
mergedAppearanceConfig.sections['seo-settings']
.config as SeoSettingsSectionConfig
)?.pages || [];
const names = pages.map(page => page.key);
export function SeoSection() {
const {isLoading} = useSeoTags(names);
if (isLoading) {
return <FullPageLoader />;
}
return (
<Fragment>
{pages.map(page => (
<TagEditorTrigger key={page.key} label={page.label} name={page.key} />
))}
</Fragment>
);
}
interface TagEditorTriggerProps {
label: MessageDescriptor;
name: string;
}
function TagEditorTrigger({label, name}: TagEditorTriggerProps) {
const {data, isLoading} = useSeoTags(names);
return (
<DialogTrigger type="modal">
<AppearanceButton disabled={isLoading}>
<Trans {...label} />
</AppearanceButton>
{data ? <TagsEditorDialog name={name} value={data[name]} /> : null}
</DialogTrigger>
);
}
interface TagsEditorDialogProps {
name: string;
value: {custom: string | null; original: string};
}
function TagsEditorDialog({name, value}: TagsEditorDialogProps) {
const {close} = useDialogContext();
const updateTags = useUpdateSeoTags(name);
const editorRef = useRef<ReactAce | null>(null);
const resetButton = (
<Button
variant="outline"
color="primary"
onClick={() => {
if (editorRef.current) {
editorRef.current.editor.setValue(value.original);
}
}}
>
<Trans message="Reset to original" />
</Button>
);
return (
<AceDialog
mode="php_laravel_blade"
title={<Trans message="Edit SEO meta tags" />}
footerStartAction={resetButton}
editorRef={editorRef}
defaultValue={value.custom || value.original}
isSaving={updateTags.isPending}
beautify={false}
onSave={newValue => {
if (newValue != null) {
updateTags.mutate(
{tags: newValue},
{
onSuccess: () => close(),
},
);
}
}}
/>
);
}

View File

@@ -0,0 +1,23 @@
import {useQuery} from '@tanstack/react-query';
import {apiClient} from '@common/http/query-client';
export function useSeoTags(name: string | string[]) {
return useQuery({
queryKey: ['admin', 'seo-tags', name],
queryFn: () => fetchTags(name),
});
}
function fetchTags(name: string | string[]) {
return apiClient
.get<
Record<
string,
{
custom: string | null;
original: string;
}
>
>(`admin/appearance/seo-tags/${name}`)
.then(response => response.data);
}

View File

@@ -0,0 +1,28 @@
import {useMutation, useQueryClient} from '@tanstack/react-query';
import {apiClient} from '@common/http/query-client';
import {BackendResponse} from '@common/http/backend-response/backend-response';
import {showHttpErrorToast} from '@common/utils/http/show-http-error-toast';
import {toast} from '@common/ui/toast/toast';
import {message} from '@common/i18n/message';
interface Response extends BackendResponse {}
export function useUpdateSeoTags(name: string) {
const queryClient = useQueryClient();
return useMutation({
mutationFn: (payload: {tags: string}) => updateTags(name, payload.tags),
onSuccess: async () => {
await queryClient.invalidateQueries({
queryKey: ['admin', 'seo-tags', name],
});
toast(message('Updated SEO tags'));
},
onError: err => showHttpErrorToast(err),
});
}
function updateTags(name: string, tags: string): Promise<Response> {
return apiClient
.put(`admin/appearance/seo-tags/${name}`, {tags})
.then(r => r.data);
}