Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[feat] 공지 기능 추가 #255

Merged
merged 11 commits into from
Sep 11, 2022
12 changes: 11 additions & 1 deletion packages/client/src/components/molecule/Shell.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,15 @@
import { config, user } from '$lib/store';
import { getDepartmentConfig, getServiceConfig } from '$lib/api/config';
import { isActivated } from '$lib/utils';
import Info from '../../icons/Info.svelte';

let clazz = '';
export { clazz as class };

let currentTime = new Date();

$: serviceConfig = $config && $config.success ? getServiceConfig($config.result) : undefined;

onMount(() => {
if (!disableBlock) {
const interval = setInterval(() => {
Expand All @@ -39,7 +42,6 @@

function isReservable(config: Config[], user: User, time: Date): boolean {
if (!user || user.isAdmin) return true;
const serviceConfig: ServiceConfig = getServiceConfig(config) as ServiceConfig;
const userDeptConfig: DepartmentConfig = getDepartmentConfig(config, user.department) as DepartmentConfig;
if (serviceConfig) {
return isActivated(serviceConfig.activateFrom as Date, serviceConfig.activateTo as Date);
Expand Down Expand Up @@ -80,6 +82,14 @@
</slot>
</section>
<section class='{mainClass} grow md:max-h-screen overflow-x-auto md:overflow-y-auto'>
{#if serviceConfig && serviceConfig.alert}
<div class='bg-primary-200 rounded-md p-6 my-4 mx-6 md:mx-8 flex gap-3'>
<Info />
<div class='grow'>
<span class='font-bold'>안내:</span> {serviceConfig.alert}
</div>
</div>
{/if}
<slot />
</section>
</main>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
let name;
let activateFrom;
let activateTo;
let alert;
let buildings;

$: if (serviceConfig) {
Expand All @@ -31,8 +32,9 @@
$: newConfig = {
id: 'SERVICE',
name,
...(activateFrom && { activateFrom: activateFrom.toISOString() }),
...(activateTo && { activateTo: activateTo.toISOString() }),
...(activateFrom ? { activateFrom: activateFrom.toISOString() } : (serviceConfig?.activateFrom && { activateFrom: null })),
...(activateTo ? { activateTo: activateTo.toISOString() } : (serviceConfig?.activateTo && { activateTo: null })),
...(alert ? { alert } : (serviceConfig.alert && { alert: null })),
buildings
};
$: isModified = !!serviceConfig && !isEqual(serviceConfig, newConfig);
Expand All @@ -48,6 +50,7 @@
activateFrom = serviceConfig?.activateFrom ?? null;
activateTo = serviceConfig?.activateTo ?? null;
buildings = structuredClone(serviceConfig?.buildings ?? {});
alert = serviceConfig?.alert ?? null;
}

function updateConfig() {
Expand All @@ -69,8 +72,8 @@

</script>

<div class='my-8 md:mx-8 flex flex-col gap-3'>
<div class='mx-6 md:mx-0 flex flex-wrap items-start'>
<div class='my-8 md:mx-4 flex flex-col gap-3'>
<div class='mx-4 md:mx-0 flex flex-wrap items-start'>
<h3>서비스 설정</h3>
<div class='grow flex justify-end gap-1'>
<Button on:click={initializeValues} disabled={!isModified ? true : undefined}
Expand Down Expand Up @@ -107,6 +110,8 @@
bind:value={activateFrom} invalidClass='text-red-800' />
<DateTimeInput class='my-2' inputClass='w-full max-w-sm' id='activate_to' label='예약 종료일' showLabel
bind:value={activateTo} invalidClass='text-red-800' />
<TextInput class='my-2' inputClass='w-full max-w-sm' id='name' label='공지사항' showLabel
bind:value={alert} />
{:else}
<div class='flex flex-col gap-1 my-2'>
<Skeleton class='md:rounded-md bg-gray-200 h-4 w-24'></Skeleton>
Expand All @@ -120,6 +125,10 @@
<Skeleton class='md:rounded-md bg-gray-200 h-4 w-24'></Skeleton>
<Skeleton class='md:rounded-md bg-gray-200 h-8 w-96'></Skeleton>
</div>
<div class='flex flex-col gap-1 my-2'>
<Skeleton class='md:rounded-md bg-gray-200 h-4 w-24'></Skeleton>
<Skeleton class='md:rounded-md bg-gray-200 h-8 w-96'></Skeleton>
</div>
{/if}
</div>
</section>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@
import LockerSectionSelector from './LockerSectionSelector.svelte';
import FloorMap from '../../atom/FloorMap.svelte';

let innerWidth: number = 0;

export let serviceConfig: ServiceConfig;
export let targetDepartmentId: string;
$: buildings = serviceConfig?.buildings ?? {};
Expand All @@ -26,8 +24,6 @@
export let reservedLockerIds: string[];
let errorData: LockerError;

let claimLoading: boolean = false;

let lockerList: { lockerId: string, disabled: boolean, reserved: boolean }[] = [];
let lockerGridHeight: number = 0;

Expand Down Expand Up @@ -66,30 +62,32 @@


<div class='w-auto h-max-content md:min-h-screen flex flex-col items-start'>
<div class='grow flex flex-col-reverse md:flex-row justify-between min-h-[280px] w-full'>
<div class='bg-[#d8dee5] md:basis-1/2 w-full md:w-1/2 md:max-w-[480px] shrink flex flex-col'>
<div
class='grow flex flex-col-reverse md:flex-row justify-between min-h-[280px] w-full py-4 md:px-8 gap-4'>
<div
class='bg-slate-200 md:basis-1/2 w-full md:w-1/2 md:max-w-[480px] items-stretch shrink flex flex-col md:rounded-xl overflow-hidden p-8'>
{#if serviceConfig && targetDepartmentId}
<LockerSectionSelector {buildings} {targetDepartmentId}
bind:selectedBuildingId
bind:selectedFloor
bind:selectedSectionId />
{:else}
<Skeleton class='rounded-lg h-10 w-48 ml-8 my-2 mt-8 bg-gray-300' />
<div class='h-5/6 flex w-full gap-2 px-8 pb-8'>
<Skeleton class='rounded-lg h-10 w-48 bg-gray-300 mb-4' />
<div class='h-5/6 flex w-full gap-2'>
<Skeleton class='h-64 rounded-xl bg-gray-300 w-1/2' />
<Skeleton class='h-64 rounded-xl bg-gray-300 w-1/2' />
</div>
{/if}
</div>
<div class='bg-slate-200 md:basis-1/2 grow'>
<div
class='w-full h-full flex flex-col md:rounded-xl overflow-hidden bg-slate-200 md:basis-1/2 grow p-8 gap-4'>
{#if serviceConfig && selectedBuildingId && selectedFloor}
<div class='p-8 w-full h-full flex justify-center items-center'>
<FloorMap class='w-full h-full aspect-[4/3]' {selectedBuildingId} {selectedFloor} {selectedSectionId} />
</div>
<h4 class='text-3xl'>배치도</h4>
<FloorMap class='grow rounded-xl aspect-[4/3] max-h-[50vh]' {selectedBuildingId} {selectedFloor}
{selectedSectionId} />
{:else}
<div class='p-8 w-full h-full flex justify-center items-center'>
<Skeleton class='w-full h-full rounded-xl bg-gray-300' />
</div>
<Skeleton class='rounded-lg h-10 w-48 bg-gray-300' />
<Skeleton class='grow rounded-xl bg-gray-300 aspect-[4/3] max-h-[50vh]' />
{/if}
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,8 @@
}
</script>

<h4 class='text-3xl py-2 pt-8 pl-4 md:pl-8'>구역 선택</h4>
<div class='h-5/6 flex gap-1 px-4 md:px-8 pb-8'>
<h4 class='text-3xl mb-2'>구역 선택</h4>
<div class='h-5/6 flex gap-1'>
<div class='basis-1/2'>
{#key `${selectedBuildingId}-${selectedFloor}`}
<SelectionListItemGroup bind:selectedIndex={selectedFloorIndex} class='h-full'>
Expand Down
41 changes: 41 additions & 0 deletions packages/client/src/icons/Info.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<script lang='ts'>
let className = undefined;
export { className as class };
export let id = undefined;
export let tabindex = undefined;
export let focusable = false;
export let title = undefined;
export let style = undefined;

$: ariaLabel = $$props['aria-label'];
$: ariaLabelledBy = $$props['aria-labelledby'];
$: labelled = ariaLabel || ariaLabelledBy || title;
$: attributes = {
'aria-label': ariaLabel,
'aria-labelledby': ariaLabelledBy,
'aria-hidden': labelled ? undefined : true,
role: labelled ? 'img' : undefined,
focusable: tabindex === '0' ? true : focusable,
tabindex
};
</script>
<svg on:click
on:focus
on:mouseover
on:mouseenter
on:mouseleave
on:keyup
on:keydown
width='24'
height='24'
fill='none'
viewBox='0 0 24 24'
xmlns='http://www.w3.org/2000/svg'
class={className}
{style}
{id}
{...attributes}>
<path
d='M12 1.999c5.524 0 10.002 4.478 10.002 10.002 0 5.523-4.478 10.001-10.002 10.001-5.524 0-10.002-4.478-10.002-10.001C1.998 6.477 6.476 1.999 12 1.999Zm-.004 8.25a1 1 0 0 0-.992.885l-.007.116.003 5.502.007.117a1 1 0 0 0 1.987-.002L13 16.75l-.003-5.501-.007-.117a1 1 0 0 0-.994-.882ZM12 6.5a1.251 1.251 0 1 0 0 2.503A1.251 1.251 0 0 0 12 6.5Z'
fill='currentColor' />
</svg>
31 changes: 20 additions & 11 deletions packages/client/src/lib/api/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ const DepartmentConfigSchema = ConfigSchema.extend({
});

const ServiceConfigSchema = ConfigSchema.extend({
buildings: BuildingSchema.default({})
buildings: z.record(BuildingSchema).default({}).optional(),
alert: z.string().optional()
});

const ConfigUpdateRequestSchema = z.object({
Expand All @@ -48,7 +49,7 @@ const ConfigUpdateRequestSchema = z.object({
activateFrom: z.string().optional(),
activateTo: z.string().optional(),
contact: z.string().optional(),
buildings: BuildingSchema.optional()
buildings: z.record(BuildingSchema).optional()
});

export async function apiGetConfig(): Promise<
Expand All @@ -70,21 +71,29 @@ export async function apiGetConfig(): Promise<
}

export function getServiceConfig(configs: Config[]): ServiceConfig {
return (
(configs.find((c: Config) => c.id === 'SERVICE') as ServiceConfig) ?? {
id: 'SERVICE',
name: null,
buildings: {}
}
);
const foundConfig = configs.find((c: Config) => c.id === 'SERVICE') as ServiceConfig;
const parsed = ServiceConfigSchema.safeParse(foundConfig);
if (parsed.success) return parsed.data as ServiceConfig;
console.error(parsed, foundConfig);
return {
id: 'SERVICE',
name: null,
buildings: {}
};
}

export function getDepartmentConfig(configs: Config[], departmentId: string): DepartmentConfig {
return configs.find((c: Config) => c.id === departmentId) as DepartmentConfig;
const foundConfig = configs.find((c: Config) => c.id === departmentId);
const parsed = DepartmentConfigSchema.safeParse(foundConfig);
if (parsed.success) return parsed.data as DepartmentConfig;
return null;
}

export function getDepartmentConfigs(configs: Config[]): DepartmentConfig[] {
return configs.filter((c: Config) => c.id !== 'SERVICE') as DepartmentConfig[];
const foundConfigs = configs.filter((c: Config) => c.id !== 'SERVICE') as DepartmentConfig[];
const parsed = z.array(DepartmentConfigSchema).safeParse(foundConfigs);
if (parsed.success) return parsed.data as DepartmentConfig[];
return null;
}

export async function apiUpdateConfig(
Expand Down
15 changes: 13 additions & 2 deletions packages/server/src/config/data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,8 @@ function toServiceConfigDao(data: ServiceConfig): ServiceConfigDao {
M: Object.fromEntries(
Object.entries(data.buildings).map(([s, b]) => [s, { M: toBuildingData(b) }])
)
}
},
...(data.alert && { a: { S: data.alert } })
};
}

Expand All @@ -125,7 +126,8 @@ function fromServiceConfigDao(dao: ServiceConfigDao): ServiceConfig {
...fromConfigDao(dao),
buildings: Object.fromEntries(
Object.entries(dao.b?.M ?? {}).map(([s, bd]) => [s, fromBuildingData(bd.M)])
)
),
...(dao.a?.S && { alert: dao.a.S })
};
}

Expand Down Expand Up @@ -236,13 +238,22 @@ export const updateConfig = async function (config: ConfigUpdateRequest) {
attributeNames['#aT'] = 'aT';
updateExp += `${updateExp ? ',' : 'SET'} #aT = :activateTo`;
}
if (config.alert) {
attributes[':alert'] = { S: config.alert };
attributeNames['#a'] = 'a';
updateExp += `${updateExp ? ',' : 'SET'} #a = :alert`;
}
if (config.activateFrom === null) {
removeExp += `${removeExp ? ',' : 'REMOVE'} aF`;
}
if (config.activateTo === null) {
attributeNames['#aT'] = 'aT';
removeExp += `${removeExp ? ',' : 'REMOVE'} #aT`;
}
if (config.alert === null) {
attributeNames['#a'] = 'a';
removeExp += `${removeExp ? ',' : 'REMOVE'} #a';`;
}
if ((config as ServiceConfigUpdateRequest).buildings) {
const buildings = (config as ServiceConfigUpdateRequest).buildings;
attributes[':buildings'] = {
Expand Down
4 changes: 4 additions & 0 deletions packages/types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,17 +115,20 @@ type ServiceConfig = Config & {
buildings: {
[buildingId: string]: Building;
};
alert?: string;
};

type ServiceConfigResponse = ConfigResponse & {
buildings: {
[buildingId: string]: Building;
};
alert?: string;
};

type ServiceConfigDao = DaoData &
ConfigDao & {
b: { M: { [buildingId: string]: { M: BuildingData } } };
a?: { S: string };
};

type Building = {
Expand Down Expand Up @@ -179,6 +182,7 @@ type ConfigUpdateRequest = {
name?: string;
activateFrom?: string | null;
activateTo?: string | null;
alert?: string | null;
};

type DepartmentConfigUpdateRequest = ConfigUpdateRequest & {
Expand Down