B2B-52: add Connected Online syncing, tables and functions (#18)
* B2B-52: add Connected Online syncing, tables and functions * clean up * improve autogenerated types * add use server directive --------- Co-authored-by: Helena <helena@Helenas-MacBook-Pro.local>
This commit is contained in:
@@ -9,6 +9,8 @@ MEDIPOST_USER=your-medipost-user
|
|||||||
MEDIPOST_PASSWORD=your-medipost-password
|
MEDIPOST_PASSWORD=your-medipost-password
|
||||||
MEDIPOST_RECIPIENT=your-medipost-recipient
|
MEDIPOST_RECIPIENT=your-medipost-recipient
|
||||||
|
|
||||||
|
CONNECTED_ONLINE_URL=your-connected-online-url
|
||||||
|
|
||||||
EMAIL_SENDER=
|
EMAIL_SENDER=
|
||||||
EMAIL_USER= # refer to your email provider's documentation
|
EMAIL_USER= # refer to your email provider's documentation
|
||||||
EMAIL_PASSWORD= # refer to your email provider's documentation
|
EMAIL_PASSWORD= # refer to your email provider's documentation
|
||||||
|
|||||||
@@ -25,26 +25,32 @@ async function syncData() {
|
|||||||
config({ path: `.env.${process.env.NODE_ENV}` });
|
config({ path: `.env.${process.env.NODE_ENV}` });
|
||||||
}
|
}
|
||||||
|
|
||||||
const baseUrl = process.env.MEDIPOST_URL!;
|
const baseUrl = process.env.MEDIPOST_URL;
|
||||||
const user = process.env.MEDIPOST_USER!;
|
const user = process.env.MEDIPOST_USER;
|
||||||
const password = process.env.MEDIPOST_PASSWORD!;
|
const password = process.env.MEDIPOST_PASSWORD;
|
||||||
const sender = process.env.MEDIPOST_MESSAGE_SENDER!;
|
const sender = process.env.MEDIPOST_MESSAGE_SENDER;
|
||||||
|
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL;
|
||||||
|
const supabaseServiceRoleKey =
|
||||||
|
process.env.NEXT_PUBLIC_SUPABASE_SERVICE_ROLE_KEY;
|
||||||
|
|
||||||
if (!baseUrl || !user || !password || !sender) {
|
if (
|
||||||
|
!baseUrl ||
|
||||||
|
!supabaseUrl ||
|
||||||
|
!supabaseServiceRoleKey ||
|
||||||
|
!user ||
|
||||||
|
!password ||
|
||||||
|
!sender
|
||||||
|
) {
|
||||||
throw new Error('Could not access all necessary environment variables');
|
throw new Error('Could not access all necessary environment variables');
|
||||||
}
|
}
|
||||||
|
|
||||||
const supabase = createCustomClient(
|
const supabase = createCustomClient(supabaseUrl, supabaseServiceRoleKey, {
|
||||||
process.env.NEXT_PUBLIC_SUPABASE_URL!,
|
|
||||||
process.env.NEXT_PUBLIC_SUPABASE_SERVICE_ROLE_KEY!,
|
|
||||||
{
|
|
||||||
auth: {
|
auth: {
|
||||||
persistSession: false,
|
persistSession: false,
|
||||||
autoRefreshToken: false,
|
autoRefreshToken: false,
|
||||||
detectSessionInUrl: false,
|
detectSessionInUrl: false,
|
||||||
},
|
},
|
||||||
},
|
});
|
||||||
);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// GET LATEST PUBLIC MESSAGE ID
|
// GET LATEST PUBLIC MESSAGE ID
|
||||||
|
|||||||
139
jobs/sync-connected-online.ts
Normal file
139
jobs/sync-connected-online.ts
Normal file
@@ -0,0 +1,139 @@
|
|||||||
|
import { createClient as createCustomClient } from '@supabase/supabase-js';
|
||||||
|
|
||||||
|
import axios from 'axios';
|
||||||
|
import { config } from 'dotenv';
|
||||||
|
|
||||||
|
async function syncData() {
|
||||||
|
if (process.env.NODE_ENV === 'local') {
|
||||||
|
config({ path: `.env.${process.env.NODE_ENV}` });
|
||||||
|
}
|
||||||
|
|
||||||
|
const baseUrl = process.env.CONNECTED_ONLINE_URL;
|
||||||
|
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL;
|
||||||
|
const supabaseServiceRoleKey =
|
||||||
|
process.env.NEXT_PUBLIC_SUPABASE_SERVICE_ROLE_KEY;
|
||||||
|
|
||||||
|
if (!baseUrl || !supabaseUrl || !supabaseServiceRoleKey) {
|
||||||
|
throw new Error('Could not access all necessary environment variables');
|
||||||
|
}
|
||||||
|
|
||||||
|
const supabase = createCustomClient(supabaseUrl, supabaseServiceRoleKey, {
|
||||||
|
auth: {
|
||||||
|
persistSession: false,
|
||||||
|
autoRefreshToken: false,
|
||||||
|
detectSessionInUrl: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await axios.post(
|
||||||
|
`${baseUrl}/Search_Load`,
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json; charset=utf-8',
|
||||||
|
},
|
||||||
|
param: "{'Value':'|et|-1'}", // get all available services in Estonian
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const responseData: {
|
||||||
|
Value: any;
|
||||||
|
Data: any;
|
||||||
|
ErrorCode: number;
|
||||||
|
ErrorMessage: string;
|
||||||
|
} = JSON.parse(response.data.d);
|
||||||
|
|
||||||
|
if (responseData?.ErrorCode !== 0) {
|
||||||
|
throw new Error('Failed to get Connected Online data');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
!responseData.Data.T_Lic?.length ||
|
||||||
|
!responseData.Data.T_Service?.length
|
||||||
|
) {
|
||||||
|
return supabase.schema('audit').from('sync_entries').insert({
|
||||||
|
operation: 'CONNECTED_ONLINE_SYNC',
|
||||||
|
comment: 'No clinic or service data received',
|
||||||
|
status: 'FAIL',
|
||||||
|
changed_by_role: 'service_role',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const clinics = responseData.Data.T_Lic;
|
||||||
|
const services = responseData.Data.T_Service;
|
||||||
|
|
||||||
|
const mappedClinics = clinics.map((clinic) => {
|
||||||
|
return {
|
||||||
|
id: clinic.ID,
|
||||||
|
can_select_worker: !!clinic.OnlineCanSelectWorker,
|
||||||
|
email: clinic.Email || null,
|
||||||
|
name: clinic.Name,
|
||||||
|
personal_code_required: !!clinic.PersonalCodeRequired,
|
||||||
|
phone_number: clinic.Phone || null,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
const mappedServices = services.map((service) => {
|
||||||
|
return {
|
||||||
|
id: service.ID,
|
||||||
|
clinic_id: service.ClinicID,
|
||||||
|
code: service.Code,
|
||||||
|
description: service.Description || null,
|
||||||
|
display: service.Display,
|
||||||
|
duration: service.Duration,
|
||||||
|
has_free_codes: !!service.HasFreeCodes,
|
||||||
|
name: service.Name,
|
||||||
|
neto_duration: service.NetoDuration,
|
||||||
|
online_hide_duration: service.OnlineHideDuration,
|
||||||
|
online_hide_price: service.OnlineHidePrice,
|
||||||
|
price: service.Price,
|
||||||
|
price_periods: service.PricePeriods || null,
|
||||||
|
requires_payment: !!service.RequiresPayment,
|
||||||
|
sync_id: service.SyncID,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
const { error: providersError } = await supabase
|
||||||
|
.from('connected_online_providers')
|
||||||
|
.upsert(mappedClinics);
|
||||||
|
|
||||||
|
const { error: servicesError } = await supabase
|
||||||
|
.from('connected_online_services')
|
||||||
|
.upsert(mappedServices, { onConflict: 'id', ignoreDuplicates: false });
|
||||||
|
|
||||||
|
if (providersError || servicesError) {
|
||||||
|
return supabase
|
||||||
|
.schema('audit')
|
||||||
|
.from('sync_entries')
|
||||||
|
.insert({
|
||||||
|
operation: 'CONNECTED_ONLINE_SYNC',
|
||||||
|
comment: providersError
|
||||||
|
? 'Error saving providers: ' + JSON.stringify(providersError)
|
||||||
|
: 'Error saving services: ' + JSON.stringify(servicesError),
|
||||||
|
status: 'FAIL',
|
||||||
|
changed_by_role: 'service_role',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
await supabase.schema('audit').from('sync_entries').insert({
|
||||||
|
operation: 'CONNECTED_ONLINE_SYNC',
|
||||||
|
status: 'SUCCESS',
|
||||||
|
changed_by_role: 'service_role',
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
await supabase
|
||||||
|
.schema('audit')
|
||||||
|
.from('sync_entries')
|
||||||
|
.insert({
|
||||||
|
operation: 'CONNECTED_ONLINE_SYNC',
|
||||||
|
status: 'FAIL',
|
||||||
|
comment: JSON.stringify(e),
|
||||||
|
changed_by_role: 'service_role',
|
||||||
|
});
|
||||||
|
throw new Error(
|
||||||
|
`Failed to sync Connected Online data, error: ${JSON.stringify(e)}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
syncData();
|
||||||
File diff suppressed because it is too large
Load Diff
48
lib/services/audit.service.ts
Normal file
48
lib/services/audit.service.ts
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
'use server'
|
||||||
|
|
||||||
|
import { createClient } from '@supabase/supabase-js';
|
||||||
|
|
||||||
|
import { RequestStatus } from '@/lib/types/audit';
|
||||||
|
import { ConnectedOnlineMethodName } from '@/lib/types/connected-online';
|
||||||
|
import { ExternalApi } from '@/lib/types/external';
|
||||||
|
import { MedipostAction } from '@/lib/types/medipost';
|
||||||
|
|
||||||
|
export default async function logRequestResult(
|
||||||
|
/* personalCode: string, */ requestApi: keyof typeof ExternalApi,
|
||||||
|
requestApiMethod: `${ConnectedOnlineMethodName}` | `${MedipostAction}`,
|
||||||
|
status: RequestStatus,
|
||||||
|
comment?: string,
|
||||||
|
startTime?: string,
|
||||||
|
serviceId?: number,
|
||||||
|
serviceProviderId?: number,
|
||||||
|
) {
|
||||||
|
const supabaseServiceUser = createClient(
|
||||||
|
process.env.NEXT_PUBLIC_SUPABASE_URL!,
|
||||||
|
process.env.NEXT_PUBLIC_SUPABASE_SERVICE_ROLE_KEY!,
|
||||||
|
{
|
||||||
|
auth: {
|
||||||
|
persistSession: false,
|
||||||
|
autoRefreshToken: false,
|
||||||
|
detectSessionInUrl: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const { error } = await supabaseServiceUser
|
||||||
|
.schema('audit')
|
||||||
|
.from('request_entries')
|
||||||
|
.insert({
|
||||||
|
/* personal_code: personalCode, */
|
||||||
|
request_api: requestApi,
|
||||||
|
request_api_method: requestApiMethod,
|
||||||
|
requested_start_date: startTime,
|
||||||
|
status,
|
||||||
|
service_id: serviceId,
|
||||||
|
service_provider_id: serviceProviderId,
|
||||||
|
comment,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
throw new Error('Failed to insert log entry, error: ' + error.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
268
lib/services/connected-online.service.ts
Normal file
268
lib/services/connected-online.service.ts
Normal file
@@ -0,0 +1,268 @@
|
|||||||
|
'use server'
|
||||||
|
|
||||||
|
import logRequestResult from '@/lib/services/audit.service';
|
||||||
|
import { RequestStatus } from '@/lib/types/audit';
|
||||||
|
import {
|
||||||
|
AvailableAppointmentsResponse,
|
||||||
|
BookTimeResponse,
|
||||||
|
ConfirmedLoadResponse,
|
||||||
|
ConnectedOnlineMethodName,
|
||||||
|
} from '@/lib/types/connected-online';
|
||||||
|
import { ExternalApi } from '@/lib/types/external';
|
||||||
|
import { Tables } from '@/supabase/database.types';
|
||||||
|
import { createClient } from '@/utils/supabase/server';
|
||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
|
export async function getAvailableAppointmentsForService(
|
||||||
|
serviceId: number,
|
||||||
|
startTime?: Date,
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
const showTimesFrom = startTime ? { StartTime: startTime } : {};
|
||||||
|
|
||||||
|
const response = await axios.post(
|
||||||
|
`${process.env.CONNECTED_ONLINE_URL!}/${ConnectedOnlineMethodName.GetAvailabilities}`,
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json; charset=utf-8',
|
||||||
|
},
|
||||||
|
param: JSON.stringify({
|
||||||
|
ServiceID: serviceId,
|
||||||
|
Key: '7T624nlu',
|
||||||
|
Lang: 'et',
|
||||||
|
...showTimesFrom,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const responseData: AvailableAppointmentsResponse = JSON.parse(
|
||||||
|
response.data.d,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (
|
||||||
|
responseData?.ErrorCode !== 0 ||
|
||||||
|
!responseData.Data.T_Service?.length ||
|
||||||
|
!responseData.Data.T_Booking?.length
|
||||||
|
) {
|
||||||
|
let comment = `Response returned error code ${responseData.ErrorCode}, message: ${responseData.ErrorMessage}`;
|
||||||
|
if (responseData?.ErrorCode === 0) {
|
||||||
|
comment = responseData.Data.T_Service?.length
|
||||||
|
? `No service present in appointment availability response, service id: ${serviceId}, start time: ${startTime}`
|
||||||
|
: `No booking times present in appointment availability response, service id: ${serviceId}, start time: ${startTime}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
await logRequestResult(
|
||||||
|
ExternalApi.ConnectedOnline,
|
||||||
|
ConnectedOnlineMethodName.GetAvailabilities,
|
||||||
|
RequestStatus.Fail,
|
||||||
|
comment,
|
||||||
|
);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
await logRequestResult(
|
||||||
|
ExternalApi.ConnectedOnline,
|
||||||
|
ConnectedOnlineMethodName.GetAvailabilities,
|
||||||
|
RequestStatus.Success,
|
||||||
|
JSON.stringify(responseData),
|
||||||
|
);
|
||||||
|
return responseData.Data;
|
||||||
|
} catch (error) {
|
||||||
|
await logRequestResult(
|
||||||
|
ExternalApi.ConnectedOnline,
|
||||||
|
ConnectedOnlineMethodName.GetAvailabilities,
|
||||||
|
RequestStatus.Fail,
|
||||||
|
JSON.stringify(error),
|
||||||
|
);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function bookAppointment(
|
||||||
|
serviceSyncId: number,
|
||||||
|
clinicId: number,
|
||||||
|
appointmentUserId: number,
|
||||||
|
syncUserID: number,
|
||||||
|
startTime: string,
|
||||||
|
locationId = 0,
|
||||||
|
comments = '',
|
||||||
|
isEarlierTimeRequested = false,
|
||||||
|
earlierTimeRequestComment = '',
|
||||||
|
) {
|
||||||
|
const supabase = await createClient();
|
||||||
|
|
||||||
|
try {
|
||||||
|
const {
|
||||||
|
data: { user },
|
||||||
|
} = await supabase.auth.getUser();
|
||||||
|
|
||||||
|
if (!user?.id) {
|
||||||
|
throw new Error('User not authenticated');
|
||||||
|
}
|
||||||
|
|
||||||
|
const [
|
||||||
|
{ data: dbClinic, error: clinicError },
|
||||||
|
{ data: dbService, error: serviceError },
|
||||||
|
] = await Promise.all([
|
||||||
|
supabase
|
||||||
|
.from('connected_online_providers')
|
||||||
|
.select('*')
|
||||||
|
.eq('id', clinicId)
|
||||||
|
.limit(1),
|
||||||
|
supabase
|
||||||
|
.from('connected_online_services')
|
||||||
|
.select('*')
|
||||||
|
.eq('sync_id', serviceSyncId)
|
||||||
|
.eq('clinic_id', clinicId)
|
||||||
|
.limit(1),
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (!dbClinic?.length || !dbService?.length) {
|
||||||
|
return logRequestResult(
|
||||||
|
ExternalApi.ConnectedOnline,
|
||||||
|
ConnectedOnlineMethodName.BookTime,
|
||||||
|
RequestStatus.Fail,
|
||||||
|
dbClinic?.length
|
||||||
|
? `Could not find clinic with id ${clinicId}, error: ${JSON.stringify(clinicError)}`
|
||||||
|
: `Could not find service with sync id ${serviceSyncId} and clinic id ${clinicId}, error: ${JSON.stringify(serviceError)}`,
|
||||||
|
startTime,
|
||||||
|
serviceSyncId,
|
||||||
|
clinicId,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const clinic: Tables<'connected_online_providers'> = dbClinic![0];
|
||||||
|
const service: Tables<'connected_online_services'> = dbService![0];
|
||||||
|
|
||||||
|
// TODO the dummy data needs to be replaced with real values once they're present on the user/account
|
||||||
|
const response = await axios.post(
|
||||||
|
`${process.env.CONNECTED_ONLINE_URL!}/${ConnectedOnlineMethodName.BookTime}`,
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json; charset=utf-8',
|
||||||
|
},
|
||||||
|
param: JSON.stringify({
|
||||||
|
EarlierTime: isEarlierTimeRequested, // once we have the e-shop functionality we can let the user select if she would like to be offered earlier time slots if they become available
|
||||||
|
EarlierTimeComment: earlierTimeRequestComment,
|
||||||
|
ClinicID: clinic.id,
|
||||||
|
ServiceID: service.id,
|
||||||
|
ClinicServiceID: service.sync_id,
|
||||||
|
UserID: appointmentUserId,
|
||||||
|
SyncUserID: syncUserID,
|
||||||
|
StartTime: startTime,
|
||||||
|
FirstName: 'Test',
|
||||||
|
LastName: 'User',
|
||||||
|
PersonalCode: '4',
|
||||||
|
Email: user.email,
|
||||||
|
Phone: 'phone',
|
||||||
|
Comments: comments,
|
||||||
|
Location: locationId,
|
||||||
|
FreeCode: '',
|
||||||
|
AddToBasket: false,
|
||||||
|
Key: '7T624nlu',
|
||||||
|
Lang: 'et', // update when integrated into app, if needed
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const responseData: BookTimeResponse = JSON.parse(response.data.d);
|
||||||
|
|
||||||
|
if (responseData?.ErrorCode !== 0 || !responseData.Value) {
|
||||||
|
return logRequestResult(
|
||||||
|
ExternalApi.ConnectedOnline,
|
||||||
|
ConnectedOnlineMethodName.BookTime,
|
||||||
|
RequestStatus.Fail,
|
||||||
|
JSON.stringify(responseData),
|
||||||
|
startTime,
|
||||||
|
service.id,
|
||||||
|
clinicId,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const responseParts = responseData.Value.split(',');
|
||||||
|
|
||||||
|
const { error } = await supabase
|
||||||
|
.from('connected_online_reservation')
|
||||||
|
.insert({
|
||||||
|
booking_code: responseParts[1],
|
||||||
|
clinic_id: clinic.id,
|
||||||
|
comments,
|
||||||
|
lang: 'et', // change later, if needed
|
||||||
|
service_id: service.id,
|
||||||
|
service_user_id: appointmentUserId,
|
||||||
|
start_time: startTime,
|
||||||
|
sync_user_id: syncUserID,
|
||||||
|
requires_payment: !!responseParts[0],
|
||||||
|
user_id: user.id,
|
||||||
|
});
|
||||||
|
|
||||||
|
await logRequestResult(
|
||||||
|
ExternalApi.ConnectedOnline,
|
||||||
|
ConnectedOnlineMethodName.BookTime,
|
||||||
|
RequestStatus.Success,
|
||||||
|
JSON.stringify(responseData),
|
||||||
|
startTime,
|
||||||
|
service.id,
|
||||||
|
clinicId,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
throw new Error(error.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
return responseData.Value;
|
||||||
|
} catch (error) {
|
||||||
|
return logRequestResult(
|
||||||
|
ExternalApi.ConnectedOnline,
|
||||||
|
ConnectedOnlineMethodName.BookTime,
|
||||||
|
RequestStatus.Fail,
|
||||||
|
JSON.stringify(error),
|
||||||
|
startTime,
|
||||||
|
serviceSyncId,
|
||||||
|
clinicId,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getConfirmedService(reservationCode: string) {
|
||||||
|
try {
|
||||||
|
const response = await axios.post(
|
||||||
|
`${process.env.CONNECTED_ONLINE_URL!}/${ConnectedOnlineMethodName.ConfirmedLoad}`,
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json; charset=utf-8',
|
||||||
|
},
|
||||||
|
param: JSON.stringify({ Value: `${reservationCode}|7T624nlu|et` }),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const responseData: ConfirmedLoadResponse = JSON.parse(response.data.d);
|
||||||
|
|
||||||
|
if (responseData?.ErrorCode !== 0) {
|
||||||
|
await logRequestResult(
|
||||||
|
ExternalApi.ConnectedOnline,
|
||||||
|
ConnectedOnlineMethodName.ConfirmedLoad,
|
||||||
|
RequestStatus.Fail,
|
||||||
|
JSON.stringify(responseData),
|
||||||
|
);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
await logRequestResult(
|
||||||
|
ExternalApi.ConnectedOnline,
|
||||||
|
ConnectedOnlineMethodName.ConfirmedLoad,
|
||||||
|
RequestStatus.Success,
|
||||||
|
JSON.stringify(responseData),
|
||||||
|
);
|
||||||
|
return responseData.Data;
|
||||||
|
} catch (error) {
|
||||||
|
await logRequestResult(
|
||||||
|
ExternalApi.ConnectedOnline,
|
||||||
|
ConnectedOnlineMethodName.ConfirmedLoad,
|
||||||
|
RequestStatus.Fail,
|
||||||
|
JSON.stringify(error),
|
||||||
|
);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
'use server';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
SupabaseClient,
|
SupabaseClient,
|
||||||
createClient as createCustomClient,
|
createClient as createCustomClient,
|
||||||
|
|||||||
@@ -2,3 +2,8 @@ export enum SyncStatus {
|
|||||||
Success = "SUCCESS",
|
Success = "SUCCESS",
|
||||||
Fail = "FAIL",
|
Fail = "FAIL",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum RequestStatus {
|
||||||
|
Success = "SUCCESS",
|
||||||
|
Fail = "FAIL",
|
||||||
|
}
|
||||||
|
|||||||
226
lib/types/connected-online.ts
Normal file
226
lib/types/connected-online.ts
Normal file
@@ -0,0 +1,226 @@
|
|||||||
|
import * as z from 'zod';
|
||||||
|
|
||||||
|
export const BookTimeResponseSchema = z.object({
|
||||||
|
Value: z.string(),
|
||||||
|
Data: z.null(),
|
||||||
|
ErrorCode: z.number(),
|
||||||
|
ErrorMessage: z.null(),
|
||||||
|
});
|
||||||
|
export type BookTimeResponse = z.infer<typeof BookTimeResponseSchema>;
|
||||||
|
|
||||||
|
export enum ConnectedOnlineMethodName {
|
||||||
|
SearchLoad = 'Search_Load',
|
||||||
|
GetAvailabilities = 'GetAvailabilities',
|
||||||
|
BookTime = 'BookTime',
|
||||||
|
ConfirmedLoad = 'Confirmed_Load',
|
||||||
|
}
|
||||||
|
|
||||||
|
export const AvailableAppointmentTBookingSchema = z.object({
|
||||||
|
ClinicID: z.string(),
|
||||||
|
LocationID: z.number(),
|
||||||
|
UserID: z.number(),
|
||||||
|
SyncUserID: z.number(),
|
||||||
|
ServiceID: z.number(),
|
||||||
|
HKServiceID: z.number(),
|
||||||
|
StartTime: z.coerce.date(),
|
||||||
|
EndTime: z.coerce.date(),
|
||||||
|
PayorCode: z.string(),
|
||||||
|
});
|
||||||
|
export type AvailableAppointment = z.infer<
|
||||||
|
typeof AvailableAppointmentTBookingSchema
|
||||||
|
>;
|
||||||
|
|
||||||
|
export const TDocsASchema = z.object({
|
||||||
|
List: z.string(),
|
||||||
|
});
|
||||||
|
export type TDocsA = z.infer<typeof TDocsASchema>;
|
||||||
|
|
||||||
|
export const TServiceSchema = z.object({
|
||||||
|
Code: z.string(),
|
||||||
|
Name: z.string(),
|
||||||
|
Price: z.number(),
|
||||||
|
VATType: z.number(),
|
||||||
|
HKServiceID: z.union([z.number(), z.null()]),
|
||||||
|
Duration: z.union([z.number(), z.null()]),
|
||||||
|
Description: z.union([z.string(), z.null()]),
|
||||||
|
OnlinePaymentRequired: z.number(),
|
||||||
|
TehikServiceCode: z.union([z.string(), z.null()]),
|
||||||
|
OnlineHideDuration: z.number(),
|
||||||
|
OnlineHidePrice: z.number(),
|
||||||
|
PricePeriods: z.string(),
|
||||||
|
});
|
||||||
|
export type TService = z.infer<typeof TServiceSchema>;
|
||||||
|
|
||||||
|
export const AvailableAppointmentsDataSchema = z.object({
|
||||||
|
T_ScheduleType: z.array(z.any()),
|
||||||
|
T_Service: z.array(TServiceSchema),
|
||||||
|
T_Booking: z.array(AvailableAppointmentTBookingSchema),
|
||||||
|
T_DocsAvailable: z.array(TDocsASchema),
|
||||||
|
T_DocsAll: z.array(TDocsASchema),
|
||||||
|
});
|
||||||
|
export type AvailableAppointmentsData = z.infer<
|
||||||
|
typeof AvailableAppointmentsDataSchema
|
||||||
|
>;
|
||||||
|
|
||||||
|
export const AvailableAppointmentsResponseSchema = z.object({
|
||||||
|
Value: z.null(),
|
||||||
|
Data: AvailableAppointmentsDataSchema,
|
||||||
|
ErrorCode: z.number(),
|
||||||
|
ErrorMessage: z.union([z.string(), z.null()]),
|
||||||
|
});
|
||||||
|
export type AvailableAppointmentsResponse = z.infer<
|
||||||
|
typeof AvailableAppointmentsResponseSchema
|
||||||
|
>;
|
||||||
|
|
||||||
|
export const APaymentRequestSchema = z.object({
|
||||||
|
ID: z.number(),
|
||||||
|
Ref: z.string(),
|
||||||
|
AmountTotal: z.number(),
|
||||||
|
AmountVat: z.number(),
|
||||||
|
PaidAmount: z.union([z.number(), z.null()]),
|
||||||
|
Paid: z.union([z.number(), z.null()]),
|
||||||
|
Failed: z.union([z.number(), z.null()]),
|
||||||
|
PaidAmount1: z.union([z.number(), z.null()]),
|
||||||
|
ScheduleID: z.number(),
|
||||||
|
PaymentID: z.union([z.number(), z.null()]),
|
||||||
|
Created: z.coerce.date(),
|
||||||
|
Nonce: z.string(),
|
||||||
|
DiscountPercent: z.number(),
|
||||||
|
DiscountCode: z.string(),
|
||||||
|
});
|
||||||
|
export type APaymentRequest = z.infer<typeof APaymentRequestSchema>;
|
||||||
|
|
||||||
|
export const PClinicSchema = z.object({
|
||||||
|
ID: z.number(),
|
||||||
|
LicenseID: z.number(),
|
||||||
|
Name: z.string(),
|
||||||
|
Email: z.string(),
|
||||||
|
Address: z.string(),
|
||||||
|
Address2: z.string(),
|
||||||
|
Phone: z.string(),
|
||||||
|
VatNo: z.string(),
|
||||||
|
RegistryCode: z.string(),
|
||||||
|
OID: z.string(),
|
||||||
|
PersonalCodeRequired: z.number(),
|
||||||
|
OnlineCommentRequired: z.number(),
|
||||||
|
OnlineLoginRequired: z.number(),
|
||||||
|
OnlineSenderEmail: z.string(),
|
||||||
|
OnlineReplyToEmail: z.string(),
|
||||||
|
OnlineCCEmail: z.string(),
|
||||||
|
OnlineRedirectkUrl: z.string(), // the typo is on their side
|
||||||
|
OnlineAllowQueue: z.number(),
|
||||||
|
});
|
||||||
|
export type PClinic = z.infer<typeof PClinicSchema>;
|
||||||
|
|
||||||
|
export const PJobTitleTranslationSchema = z.object({
|
||||||
|
ID: z.number(),
|
||||||
|
SyncID: z.number(),
|
||||||
|
TextEN: z.string(),
|
||||||
|
TextET: z.string(),
|
||||||
|
TextFI: z.string(),
|
||||||
|
TextRU: z.string(),
|
||||||
|
TextLT: z.string(),
|
||||||
|
ClinicID: z.number(),
|
||||||
|
Deleted: z.number(),
|
||||||
|
});
|
||||||
|
export type PJobTitleTranslation = z.infer<typeof PJobTitleTranslationSchema>;
|
||||||
|
|
||||||
|
export const PServiceSchema = z.object({
|
||||||
|
ID: z.number(),
|
||||||
|
NameET: z.string(),
|
||||||
|
NameEN: z.string(),
|
||||||
|
NameRU: z.string(),
|
||||||
|
NameFI: z.string(),
|
||||||
|
DescriptionET: z.string(),
|
||||||
|
DescriptionEN: z.string(),
|
||||||
|
DescriptionRU: z.string(),
|
||||||
|
DescriptionFI: z.string(),
|
||||||
|
ExtraEmailTextET: z.string(),
|
||||||
|
ExtraEmailTextEN: z.string(),
|
||||||
|
ExtraEmailTextRU: z.string(),
|
||||||
|
ExtraEmailTextFI: z.string(),
|
||||||
|
});
|
||||||
|
export type PService = z.infer<typeof PServiceSchema>;
|
||||||
|
|
||||||
|
export const ParamSchema = z.object({
|
||||||
|
PersonalCodeRequired: z.number(),
|
||||||
|
OnlineCommentRequired: z.number(),
|
||||||
|
OnlineLoginRequired: z.number(),
|
||||||
|
ClinicName: z.string(),
|
||||||
|
ClinicID: z.number(),
|
||||||
|
OnlineRedirectkUrl: z.string(),
|
||||||
|
OnlineAllowQueue: z.number(),
|
||||||
|
Key: z.string(),
|
||||||
|
});
|
||||||
|
export type Param = z.infer<typeof ParamSchema>;
|
||||||
|
|
||||||
|
export const TBookingSchema = z.object({
|
||||||
|
ID: z.number(),
|
||||||
|
ClinicID: z.number(),
|
||||||
|
FirstName: z.string(),
|
||||||
|
LastName: z.string(),
|
||||||
|
Email: z.string(),
|
||||||
|
Phone: z.string(),
|
||||||
|
PersonalCode: z.string(),
|
||||||
|
Comments: z.string(),
|
||||||
|
ServiceName: z.string(),
|
||||||
|
DoctorName: z.string(),
|
||||||
|
StartTime: z.coerce.date(),
|
||||||
|
EndTime: z.coerce.date(),
|
||||||
|
Status: z.number(),
|
||||||
|
PaymentRequestID: z.number(),
|
||||||
|
MailResponse: z.null(), // was not present in test data, might need to be specified in the future
|
||||||
|
BookingCode: z.string(),
|
||||||
|
LocationName: z.union([z.string(), z.null()]),
|
||||||
|
isDR: z.number(),
|
||||||
|
LocationID: z.number(),
|
||||||
|
ScheduleID: z.number(),
|
||||||
|
ServiceID: z.number(),
|
||||||
|
ServiceSyncID: z.number(),
|
||||||
|
ServiceCode: z.string(),
|
||||||
|
ServiceIsRemote: z.number(),
|
||||||
|
IsRegistered: z.number(),
|
||||||
|
OfferEarlierTime: z.number(),
|
||||||
|
EarlierTimeComment: z.string(),
|
||||||
|
PartnerCode: z.union([z.number(), z.null()]),
|
||||||
|
PartnerCallBackUrl: z.union([z.string(), z.null()]),
|
||||||
|
LocationOfficialName: z.union([z.string(), z.null()]),
|
||||||
|
LocationAddress1: z.union([z.string(), z.null()]),
|
||||||
|
LocationAddress2: z.union([z.string(), z.null()]),
|
||||||
|
LocationPhone: z.union([z.string(), z.null()]),
|
||||||
|
});
|
||||||
|
export type TBooking = z.infer<typeof TBookingSchema>;
|
||||||
|
|
||||||
|
export const TDoctorSchema = z.object({
|
||||||
|
ID: z.number(),
|
||||||
|
Name: z.string(),
|
||||||
|
Prefix: z.string(),
|
||||||
|
Photo: z.string(),
|
||||||
|
SpokenLanguages: z.string(),
|
||||||
|
JobTitleID: z.union([z.number(), z.null()]),
|
||||||
|
ClinicID: z.number(),
|
||||||
|
Deleted: z.number(),
|
||||||
|
});
|
||||||
|
export type TDoctor = z.infer<typeof TDoctorSchema>;
|
||||||
|
|
||||||
|
export const ConfirmedLoadDataSchema = z.object({
|
||||||
|
T_Booking: z.array(TBookingSchema),
|
||||||
|
P_Location: z.array(z.any()),
|
||||||
|
P_Clinic: z.array(PClinicSchema),
|
||||||
|
A_PaymentRequest: z.array(APaymentRequestSchema),
|
||||||
|
P_Service: z.array(PServiceSchema),
|
||||||
|
T_Service: z.array(z.any()),
|
||||||
|
T_ServiceHK: z.array(z.any()),
|
||||||
|
P_JobTitleTranslations: z.array(PJobTitleTranslationSchema),
|
||||||
|
T_Doctor: z.array(TDoctorSchema),
|
||||||
|
Params: z.array(ParamSchema),
|
||||||
|
});
|
||||||
|
export type ConfirmedLoadData = z.infer<typeof ConfirmedLoadDataSchema>;
|
||||||
|
|
||||||
|
export const ConfirmedLoadResponseSchema = z.object({
|
||||||
|
Value: z.null(),
|
||||||
|
Data: ConfirmedLoadDataSchema,
|
||||||
|
ErrorCode: z.number(),
|
||||||
|
ErrorMessage: z.union([z.string(), z.null()]),
|
||||||
|
});
|
||||||
|
export type ConfirmedLoadResponse = z.infer<typeof ConfirmedLoadResponseSchema>;
|
||||||
4
lib/types/external.ts
Normal file
4
lib/types/external.ts
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
export enum ExternalApi {
|
||||||
|
Medipost = 'Medipost',
|
||||||
|
ConnectedOnline = 'ConnectedOnline',
|
||||||
|
}
|
||||||
27
package.json
27
package.json
@@ -19,21 +19,22 @@
|
|||||||
"supabase": "supabase",
|
"supabase": "supabase",
|
||||||
"supabase:start": "supabase status || supabase start",
|
"supabase:start": "supabase status || supabase start",
|
||||||
"supabase:stop": "supabase stop",
|
"supabase:stop": "supabase stop",
|
||||||
"supabase:reset": "supabase db reset",
|
|
||||||
"supabase:status": "supabase status",
|
"supabase:status": "supabase status",
|
||||||
"supabase:test": "supabase db test",
|
"supabase:test": "supabase db test",
|
||||||
|
"supabase:db:reset": "supabase db reset",
|
||||||
"supabase:db:lint": "supabase db lint",
|
"supabase:db:lint": "supabase db lint",
|
||||||
"supabase:db:diff": "supabase db diff",
|
"supabase:db:diff": "supabase db diff",
|
||||||
"supabase:deploy": "supabase link --project-ref $SUPABASE_PROJECT_REF && supabase db push",
|
"supabase:deploy": "supabase link --project-ref $SUPABASE_PROJECT_REF && supabase db push",
|
||||||
"supabase:typegen": "pnpm run supabase:typegen:packages && pnpm run supabase:typegen:app",
|
"supabase:typegen": "pnpm run supabase:typegen:packages && pnpm run supabase:typegen:app",
|
||||||
"supabase:typegen:packages": "supabase gen types typescript --local > ../../packages/supabase/src/database.types.ts",
|
"supabase:typegen:packages": "supabase gen types typescript --local > ../../packages/supabase/src/database.types.ts",
|
||||||
"supabase:typegen:app": "supabase gen types typescript --local > ./lib/database.types.ts",
|
"supabase:typegen:app": "supabase gen types typescript --local > ./supabase/database.types.ts",
|
||||||
"supabase:db:dump:local": "supabase db dump --local --data-only",
|
"supabase:db:dump:local": "supabase db dump --local --data-only",
|
||||||
"sync-data:dev": "NODE_ENV=local ts-node jobs/sync-analysis-groups.ts"
|
"sync-analysis-groups:dev": "NODE_ENV=local ts-node jobs/sync-analysis-groups.ts",
|
||||||
|
"sync-connected-online:dev": "NODE_ENV=local ts-node jobs/sync-connected-online.ts"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@edge-csrf/nextjs": "2.5.3-cloudflare-rc1",
|
"@edge-csrf/nextjs": "2.5.3-cloudflare-rc1",
|
||||||
"@hookform/resolvers": "^5.0.1",
|
"@hookform/resolvers": "^5.1.1",
|
||||||
"@kit/accounts": "workspace:*",
|
"@kit/accounts": "workspace:*",
|
||||||
"@kit/admin": "workspace:*",
|
"@kit/admin": "workspace:*",
|
||||||
"@kit/analytics": "workspace:*",
|
"@kit/analytics": "workspace:*",
|
||||||
@@ -61,7 +62,7 @@
|
|||||||
"@supabase/supabase-js": "2.49.4",
|
"@supabase/supabase-js": "2.49.4",
|
||||||
"@tanstack/react-query": "5.76.1",
|
"@tanstack/react-query": "5.76.1",
|
||||||
"@tanstack/react-table": "^8.21.3",
|
"@tanstack/react-table": "^8.21.3",
|
||||||
"axios": "^1.9.0",
|
"axios": "^1.10.0",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"date-fns": "^4.1.0",
|
"date-fns": "^4.1.0",
|
||||||
"fast-xml-parser": "^5.2.5",
|
"fast-xml-parser": "^5.2.5",
|
||||||
@@ -72,13 +73,13 @@
|
|||||||
"next-themes": "0.4.6",
|
"next-themes": "0.4.6",
|
||||||
"react": "19.1.0",
|
"react": "19.1.0",
|
||||||
"react-dom": "19.1.0",
|
"react-dom": "19.1.0",
|
||||||
"react-hook-form": "^7.56.3",
|
"react-hook-form": "^7.58.0",
|
||||||
"react-i18next": "^15.5.1",
|
"react-i18next": "^15.5.3",
|
||||||
"recharts": "2.15.3",
|
"recharts": "2.15.3",
|
||||||
"sonner": "^2.0.3",
|
"sonner": "^2.0.5",
|
||||||
"tailwind-merge": "^3.3.0",
|
"tailwind-merge": "^3.3.1",
|
||||||
"ts-node": "^10.9.2",
|
"ts-node": "^10.9.2",
|
||||||
"zod": "^3.24.4"
|
"zod": "^3.25.67"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@hookform/resolvers": "^5.0.1",
|
"@hookform/resolvers": "^5.0.1",
|
||||||
@@ -86,9 +87,9 @@
|
|||||||
"@kit/prettier-config": "workspace:*",
|
"@kit/prettier-config": "workspace:*",
|
||||||
"@kit/tsconfig": "workspace:*",
|
"@kit/tsconfig": "workspace:*",
|
||||||
"@next/bundle-analyzer": "15.3.2",
|
"@next/bundle-analyzer": "15.3.2",
|
||||||
"@tailwindcss/postcss": "^4.1.7",
|
"@tailwindcss/postcss": "^4.1.10",
|
||||||
"@types/lodash": "^4.17.17",
|
"@types/lodash": "^4.17.17",
|
||||||
"@types/node": "^22.15.18",
|
"@types/node": "^22.15.32",
|
||||||
"@types/react": "19.1.4",
|
"@types/react": "19.1.4",
|
||||||
"@types/react-dom": "19.1.5",
|
"@types/react-dom": "19.1.5",
|
||||||
"babel-plugin-react-compiler": "19.1.0-rc.2",
|
"babel-plugin-react-compiler": "19.1.0-rc.2",
|
||||||
@@ -97,7 +98,7 @@
|
|||||||
"pino-pretty": "^13.0.0",
|
"pino-pretty": "^13.0.0",
|
||||||
"prettier": "^3.5.3",
|
"prettier": "^3.5.3",
|
||||||
"react-hook-form": "^7.57.0",
|
"react-hook-form": "^7.57.0",
|
||||||
"supabase": "^2.22.12",
|
"supabase": "^2.26.9",
|
||||||
"tailwindcss": "4.1.7",
|
"tailwindcss": "4.1.7",
|
||||||
"tailwindcss-animate": "^1.0.7",
|
"tailwindcss-animate": "^1.0.7",
|
||||||
"typescript": "^5.8.3",
|
"typescript": "^5.8.3",
|
||||||
|
|||||||
574
pnpm-lock.yaml
generated
574
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -48,6 +48,48 @@ export type Database = {
|
|||||||
}
|
}
|
||||||
Relationships: []
|
Relationships: []
|
||||||
}
|
}
|
||||||
|
request_entries: {
|
||||||
|
Row: {
|
||||||
|
comment: string | null
|
||||||
|
created_at: string
|
||||||
|
id: number
|
||||||
|
personal_code: number | null
|
||||||
|
request_api: string
|
||||||
|
request_api_method: string
|
||||||
|
requested_end_date: string | null
|
||||||
|
requested_start_date: string | null
|
||||||
|
service_id: number | null
|
||||||
|
service_provider_id: number | null
|
||||||
|
status: Database["audit"]["Enums"]["request_status"]
|
||||||
|
}
|
||||||
|
Insert: {
|
||||||
|
comment?: string | null
|
||||||
|
created_at?: string
|
||||||
|
id?: number
|
||||||
|
personal_code?: number | null
|
||||||
|
request_api: string
|
||||||
|
request_api_method: string
|
||||||
|
requested_end_date?: string | null
|
||||||
|
requested_start_date?: string | null
|
||||||
|
service_id?: number | null
|
||||||
|
service_provider_id?: number | null
|
||||||
|
status: Database["audit"]["Enums"]["request_status"]
|
||||||
|
}
|
||||||
|
Update: {
|
||||||
|
comment?: string | null
|
||||||
|
created_at?: string
|
||||||
|
id?: number
|
||||||
|
personal_code?: number | null
|
||||||
|
request_api?: string
|
||||||
|
request_api_method?: string
|
||||||
|
requested_end_date?: string | null
|
||||||
|
requested_start_date?: string | null
|
||||||
|
service_id?: number | null
|
||||||
|
service_provider_id?: number | null
|
||||||
|
status?: Database["audit"]["Enums"]["request_status"]
|
||||||
|
}
|
||||||
|
Relationships: []
|
||||||
|
}
|
||||||
sync_entries: {
|
sync_entries: {
|
||||||
Row: {
|
Row: {
|
||||||
changed_by_role: string
|
changed_by_role: string
|
||||||
@@ -83,6 +125,7 @@ export type Database = {
|
|||||||
[_ in never]: never
|
[_ in never]: never
|
||||||
}
|
}
|
||||||
Enums: {
|
Enums: {
|
||||||
|
request_status: "SUCCESS" | "FAIL"
|
||||||
sync_status: "SUCCESS" | "FAIL"
|
sync_status: "SUCCESS" | "FAIL"
|
||||||
}
|
}
|
||||||
CompositeTypes: {
|
CompositeTypes: {
|
||||||
@@ -596,6 +639,158 @@ export type Database = {
|
|||||||
}
|
}
|
||||||
Relationships: []
|
Relationships: []
|
||||||
}
|
}
|
||||||
|
connected_online_providers: {
|
||||||
|
Row: {
|
||||||
|
can_select_worker: boolean
|
||||||
|
created_at: string
|
||||||
|
email: string | null
|
||||||
|
id: number
|
||||||
|
name: string
|
||||||
|
personal_code_required: boolean
|
||||||
|
phone_number: string | null
|
||||||
|
updated_at: string | null
|
||||||
|
}
|
||||||
|
Insert: {
|
||||||
|
can_select_worker: boolean
|
||||||
|
created_at?: string
|
||||||
|
email?: string | null
|
||||||
|
id: number
|
||||||
|
name: string
|
||||||
|
personal_code_required: boolean
|
||||||
|
phone_number?: string | null
|
||||||
|
updated_at?: string | null
|
||||||
|
}
|
||||||
|
Update: {
|
||||||
|
can_select_worker?: boolean
|
||||||
|
created_at?: string
|
||||||
|
email?: string | null
|
||||||
|
id?: number
|
||||||
|
name?: string
|
||||||
|
personal_code_required?: boolean
|
||||||
|
phone_number?: string | null
|
||||||
|
updated_at?: string | null
|
||||||
|
}
|
||||||
|
Relationships: []
|
||||||
|
}
|
||||||
|
connected_online_reservation: {
|
||||||
|
Row: {
|
||||||
|
booking_code: string
|
||||||
|
clinic_id: number
|
||||||
|
comments: string | null
|
||||||
|
created_at: string
|
||||||
|
discount_code: string | null
|
||||||
|
id: number
|
||||||
|
lang: string
|
||||||
|
requires_payment: boolean
|
||||||
|
service_id: number
|
||||||
|
service_user_id: number | null
|
||||||
|
start_time: string
|
||||||
|
sync_user_id: number
|
||||||
|
updated_at: string | null
|
||||||
|
user_id: string
|
||||||
|
}
|
||||||
|
Insert: {
|
||||||
|
booking_code: string
|
||||||
|
clinic_id: number
|
||||||
|
comments?: string | null
|
||||||
|
created_at?: string
|
||||||
|
discount_code?: string | null
|
||||||
|
id?: number
|
||||||
|
lang: string
|
||||||
|
requires_payment: boolean
|
||||||
|
service_id: number
|
||||||
|
service_user_id?: number | null
|
||||||
|
start_time: string
|
||||||
|
sync_user_id: number
|
||||||
|
updated_at?: string | null
|
||||||
|
user_id: string
|
||||||
|
}
|
||||||
|
Update: {
|
||||||
|
booking_code?: string
|
||||||
|
clinic_id?: number
|
||||||
|
comments?: string | null
|
||||||
|
created_at?: string
|
||||||
|
discount_code?: string | null
|
||||||
|
id?: number
|
||||||
|
lang?: string
|
||||||
|
requires_payment?: boolean
|
||||||
|
service_id?: number
|
||||||
|
service_user_id?: number | null
|
||||||
|
start_time?: string
|
||||||
|
sync_user_id?: number
|
||||||
|
updated_at?: string | null
|
||||||
|
user_id?: string
|
||||||
|
}
|
||||||
|
Relationships: []
|
||||||
|
}
|
||||||
|
connected_online_services: {
|
||||||
|
Row: {
|
||||||
|
clinic_id: number
|
||||||
|
code: string
|
||||||
|
created_at: string
|
||||||
|
description: string | null
|
||||||
|
display: string | null
|
||||||
|
duration: number
|
||||||
|
has_free_codes: boolean
|
||||||
|
id: number
|
||||||
|
name: string
|
||||||
|
neto_duration: number | null
|
||||||
|
online_hide_duration: number | null
|
||||||
|
online_hide_price: number | null
|
||||||
|
price: number
|
||||||
|
price_periods: string | null
|
||||||
|
requires_payment: boolean
|
||||||
|
sync_id: number
|
||||||
|
updated_at: string | null
|
||||||
|
}
|
||||||
|
Insert: {
|
||||||
|
clinic_id: number
|
||||||
|
code: string
|
||||||
|
created_at?: string
|
||||||
|
description?: string | null
|
||||||
|
display?: string | null
|
||||||
|
duration: number
|
||||||
|
has_free_codes: boolean
|
||||||
|
id: number
|
||||||
|
name: string
|
||||||
|
neto_duration?: number | null
|
||||||
|
online_hide_duration?: number | null
|
||||||
|
online_hide_price?: number | null
|
||||||
|
price: number
|
||||||
|
price_periods?: string | null
|
||||||
|
requires_payment: boolean
|
||||||
|
sync_id: number
|
||||||
|
updated_at?: string | null
|
||||||
|
}
|
||||||
|
Update: {
|
||||||
|
clinic_id?: number
|
||||||
|
code?: string
|
||||||
|
created_at?: string
|
||||||
|
description?: string | null
|
||||||
|
display?: string | null
|
||||||
|
duration?: number
|
||||||
|
has_free_codes?: boolean
|
||||||
|
id?: number
|
||||||
|
name?: string
|
||||||
|
neto_duration?: number | null
|
||||||
|
online_hide_duration?: number | null
|
||||||
|
online_hide_price?: number | null
|
||||||
|
price?: number
|
||||||
|
price_periods?: string | null
|
||||||
|
requires_payment?: boolean
|
||||||
|
sync_id?: number
|
||||||
|
updated_at?: string | null
|
||||||
|
}
|
||||||
|
Relationships: [
|
||||||
|
{
|
||||||
|
foreignKeyName: "connected_online_services_clinic_id_fkey"
|
||||||
|
columns: ["clinic_id"]
|
||||||
|
isOneToOne: false
|
||||||
|
referencedRelation: "connected_online_providers"
|
||||||
|
referencedColumns: ["id"]
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
invitations: {
|
invitations: {
|
||||||
Row: {
|
Row: {
|
||||||
account_id: string
|
account_id: string
|
||||||
@@ -661,6 +856,63 @@ export type Database = {
|
|||||||
},
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
nonces: {
|
||||||
|
Row: {
|
||||||
|
client_token: string
|
||||||
|
created_at: string
|
||||||
|
expires_at: string
|
||||||
|
id: string
|
||||||
|
last_verification_at: string | null
|
||||||
|
last_verification_ip: unknown | null
|
||||||
|
last_verification_user_agent: string | null
|
||||||
|
metadata: Json | null
|
||||||
|
nonce: string
|
||||||
|
purpose: string
|
||||||
|
revoked: boolean
|
||||||
|
revoked_reason: string | null
|
||||||
|
scopes: string[] | null
|
||||||
|
used_at: string | null
|
||||||
|
user_id: string | null
|
||||||
|
verification_attempts: number
|
||||||
|
}
|
||||||
|
Insert: {
|
||||||
|
client_token: string
|
||||||
|
created_at?: string
|
||||||
|
expires_at: string
|
||||||
|
id?: string
|
||||||
|
last_verification_at?: string | null
|
||||||
|
last_verification_ip?: unknown | null
|
||||||
|
last_verification_user_agent?: string | null
|
||||||
|
metadata?: Json | null
|
||||||
|
nonce: string
|
||||||
|
purpose: string
|
||||||
|
revoked?: boolean
|
||||||
|
revoked_reason?: string | null
|
||||||
|
scopes?: string[] | null
|
||||||
|
used_at?: string | null
|
||||||
|
user_id?: string | null
|
||||||
|
verification_attempts?: number
|
||||||
|
}
|
||||||
|
Update: {
|
||||||
|
client_token?: string
|
||||||
|
created_at?: string
|
||||||
|
expires_at?: string
|
||||||
|
id?: string
|
||||||
|
last_verification_at?: string | null
|
||||||
|
last_verification_ip?: unknown | null
|
||||||
|
last_verification_user_agent?: string | null
|
||||||
|
metadata?: Json | null
|
||||||
|
nonce?: string
|
||||||
|
purpose?: string
|
||||||
|
revoked?: boolean
|
||||||
|
revoked_reason?: string | null
|
||||||
|
scopes?: string[] | null
|
||||||
|
used_at?: string | null
|
||||||
|
user_id?: string | null
|
||||||
|
verification_attempts?: number
|
||||||
|
}
|
||||||
|
Relationships: []
|
||||||
|
}
|
||||||
notifications: {
|
notifications: {
|
||||||
Row: {
|
Row: {
|
||||||
account_id: string
|
account_id: string
|
||||||
@@ -1058,6 +1310,17 @@ export type Database = {
|
|||||||
updated_at: string
|
updated_at: string
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
create_nonce: {
|
||||||
|
Args: {
|
||||||
|
p_user_id?: string
|
||||||
|
p_purpose?: string
|
||||||
|
p_expires_in_seconds?: number
|
||||||
|
p_metadata?: Json
|
||||||
|
p_scopes?: string[]
|
||||||
|
p_revoke_previous?: boolean
|
||||||
|
}
|
||||||
|
Returns: Json
|
||||||
|
}
|
||||||
create_team_account: {
|
create_team_account: {
|
||||||
Args: { account_name: string }
|
Args: { account_name: string }
|
||||||
Returns: {
|
Returns: {
|
||||||
@@ -1110,6 +1373,10 @@ export type Database = {
|
|||||||
Args: Record<PropertyKey, never>
|
Args: Record<PropertyKey, never>
|
||||||
Returns: Json
|
Returns: Json
|
||||||
}
|
}
|
||||||
|
get_nonce_status: {
|
||||||
|
Args: { p_id: string }
|
||||||
|
Returns: Json
|
||||||
|
}
|
||||||
get_upper_system_role: {
|
get_upper_system_role: {
|
||||||
Args: Record<PropertyKey, never>
|
Args: Record<PropertyKey, never>
|
||||||
Returns: string
|
Returns: string
|
||||||
@@ -1146,6 +1413,10 @@ export type Database = {
|
|||||||
}
|
}
|
||||||
Returns: boolean
|
Returns: boolean
|
||||||
}
|
}
|
||||||
|
is_aal2: {
|
||||||
|
Args: Record<PropertyKey, never>
|
||||||
|
Returns: boolean
|
||||||
|
}
|
||||||
is_account_owner: {
|
is_account_owner: {
|
||||||
Args: { account_id: string }
|
Args: { account_id: string }
|
||||||
Returns: boolean
|
Returns: boolean
|
||||||
@@ -1154,14 +1425,26 @@ export type Database = {
|
|||||||
Args: { target_account_id: string }
|
Args: { target_account_id: string }
|
||||||
Returns: boolean
|
Returns: boolean
|
||||||
}
|
}
|
||||||
|
is_mfa_compliant: {
|
||||||
|
Args: Record<PropertyKey, never>
|
||||||
|
Returns: boolean
|
||||||
|
}
|
||||||
is_set: {
|
is_set: {
|
||||||
Args: { field_name: string }
|
Args: { field_name: string }
|
||||||
Returns: boolean
|
Returns: boolean
|
||||||
}
|
}
|
||||||
|
is_super_admin: {
|
||||||
|
Args: Record<PropertyKey, never>
|
||||||
|
Returns: boolean
|
||||||
|
}
|
||||||
is_team_member: {
|
is_team_member: {
|
||||||
Args: { account_id: string; user_id: string }
|
Args: { account_id: string; user_id: string }
|
||||||
Returns: boolean
|
Returns: boolean
|
||||||
}
|
}
|
||||||
|
revoke_nonce: {
|
||||||
|
Args: { p_id: string; p_reason?: string }
|
||||||
|
Returns: boolean
|
||||||
|
}
|
||||||
team_account_workspace: {
|
team_account_workspace: {
|
||||||
Args: { account_slug: string }
|
Args: { account_slug: string }
|
||||||
Returns: {
|
Returns: {
|
||||||
@@ -1236,6 +1519,18 @@ export type Database = {
|
|||||||
updated_at: string
|
updated_at: string
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
verify_nonce: {
|
||||||
|
Args: {
|
||||||
|
p_token: string
|
||||||
|
p_purpose: string
|
||||||
|
p_user_id?: string
|
||||||
|
p_required_scopes?: string[]
|
||||||
|
p_max_verification_attempts?: number
|
||||||
|
p_ip?: unknown
|
||||||
|
p_user_agent?: string
|
||||||
|
}
|
||||||
|
Returns: Json
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Enums: {
|
Enums: {
|
||||||
analysis_order_status:
|
analysis_order_status:
|
||||||
@@ -1383,6 +1678,7 @@ export type CompositeTypes<
|
|||||||
export const Constants = {
|
export const Constants = {
|
||||||
audit: {
|
audit: {
|
||||||
Enums: {
|
Enums: {
|
||||||
|
request_status: ["SUCCESS", "FAIL"],
|
||||||
sync_status: ["SUCCESS", "FAIL"],
|
sync_status: ["SUCCESS", "FAIL"],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -0,0 +1,227 @@
|
|||||||
|
create table "public"."connected_online_providers" (
|
||||||
|
"id" bigint not null,
|
||||||
|
"name" text not null,
|
||||||
|
"email" text,
|
||||||
|
"phone_number" text,
|
||||||
|
"can_select_worker" boolean not null,
|
||||||
|
"personal_code_required" boolean not null,
|
||||||
|
"created_at" timestamp with time zone not null default now(),
|
||||||
|
"updated_at" timestamp without time zone default now()
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
alter table "public"."connected_online_providers" enable row level security;
|
||||||
|
|
||||||
|
create table "public"."connected_online_services" (
|
||||||
|
"id" bigint not null,
|
||||||
|
"clinic_id" bigint not null,
|
||||||
|
"sync_id" bigint not null,
|
||||||
|
"name" text not null,
|
||||||
|
"description" text,
|
||||||
|
"price" double precision not null,
|
||||||
|
"requires_payment" boolean not null,
|
||||||
|
"duration" bigint not null,
|
||||||
|
"neto_duration" bigint,
|
||||||
|
"display" text,
|
||||||
|
"price_periods" text,
|
||||||
|
"online_hide_duration" bigint,
|
||||||
|
"online_hide_price" bigint,
|
||||||
|
"code" text not null,
|
||||||
|
"has_free_codes" boolean not null,
|
||||||
|
"created_at" timestamp with time zone not null default now(),
|
||||||
|
"updated_at" timestamp with time zone default now()
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
alter table "public"."connected_online_services" enable row level security;
|
||||||
|
|
||||||
|
CREATE UNIQUE INDEX connected_online_providers_id_key ON public.connected_online_providers USING btree (id);
|
||||||
|
|
||||||
|
CREATE UNIQUE INDEX connected_online_providers_pkey ON public.connected_online_providers USING btree (id);
|
||||||
|
|
||||||
|
CREATE UNIQUE INDEX connected_online_services_id_key ON public.connected_online_services USING btree (id);
|
||||||
|
|
||||||
|
CREATE UNIQUE INDEX connected_online_services_pkey ON public.connected_online_services USING btree (id);
|
||||||
|
|
||||||
|
alter table "public"."connected_online_providers" add constraint "connected_online_providers_pkey" PRIMARY KEY using index "connected_online_providers_pkey";
|
||||||
|
|
||||||
|
alter table "public"."connected_online_services" add constraint "connected_online_services_pkey" PRIMARY KEY using index "connected_online_services_pkey";
|
||||||
|
|
||||||
|
alter table "public"."connected_online_providers" add constraint "connected_online_providers_id_key" UNIQUE using index "connected_online_providers_id_key";
|
||||||
|
|
||||||
|
alter table "public"."connected_online_services" add constraint "connected_online_services_clinic_id_fkey" FOREIGN KEY (clinic_id) REFERENCES connected_online_providers(id) ON UPDATE CASCADE ON DELETE CASCADE not valid;
|
||||||
|
|
||||||
|
alter table "public"."connected_online_services" validate constraint "connected_online_services_clinic_id_fkey";
|
||||||
|
|
||||||
|
alter table "public"."connected_online_services" add constraint "connected_online_services_id_key" UNIQUE using index "connected_online_services_id_key";
|
||||||
|
|
||||||
|
grant delete on table "public"."connected_online_providers" to "service_role";
|
||||||
|
|
||||||
|
grant insert on table "public"."connected_online_providers" to "service_role";
|
||||||
|
|
||||||
|
grant references on table "public"."connected_online_providers" to "service_role";
|
||||||
|
|
||||||
|
grant select on table "public"."connected_online_providers" to "service_role";
|
||||||
|
|
||||||
|
grant trigger on table "public"."connected_online_providers" to "service_role";
|
||||||
|
|
||||||
|
grant truncate on table "public"."connected_online_providers" to "service_role";
|
||||||
|
|
||||||
|
grant update on table "public"."connected_online_providers" to "service_role";
|
||||||
|
|
||||||
|
grant select on table "public"."connected_online_providers" to "authenticated";
|
||||||
|
|
||||||
|
grant delete on table "public"."connected_online_services" to "service_role";
|
||||||
|
|
||||||
|
grant insert on table "public"."connected_online_services" to "service_role";
|
||||||
|
|
||||||
|
grant references on table "public"."connected_online_services" to "service_role";
|
||||||
|
|
||||||
|
grant select on table "public"."connected_online_services" to "service_role";
|
||||||
|
|
||||||
|
grant trigger on table "public"."connected_online_services" to "service_role";
|
||||||
|
|
||||||
|
grant truncate on table "public"."connected_online_services" to "service_role";
|
||||||
|
|
||||||
|
grant update on table "public"."connected_online_services" to "service_role";
|
||||||
|
|
||||||
|
grant select on table "public"."connected_online_services" to "authenticated";
|
||||||
|
|
||||||
|
create type "audit"."request_status" as enum ('SUCCESS', 'FAIL');
|
||||||
|
|
||||||
|
create table "audit"."request_entries" (
|
||||||
|
"id" bigint generated by default as identity not null,
|
||||||
|
"personal_code" bigint,
|
||||||
|
"request_api" text not null,
|
||||||
|
"request_api_method" text not null,
|
||||||
|
"status" audit.request_status not null,
|
||||||
|
"comment" text,
|
||||||
|
"service_provider_id" bigint,
|
||||||
|
"service_id" bigint,
|
||||||
|
"requested_start_date" timestamp with time zone,
|
||||||
|
"requested_end_date" timestamp with time zone,
|
||||||
|
"created_at" timestamp with time zone not null default now()
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
alter table "audit"."request_entries" enable row level security;
|
||||||
|
|
||||||
|
CREATE UNIQUE INDEX request_entries_pkey ON audit.request_entries USING btree (id);
|
||||||
|
|
||||||
|
alter table "audit"."request_entries" add constraint "request_entries_pkey" PRIMARY KEY using index "request_entries_pkey";
|
||||||
|
|
||||||
|
grant delete on table "audit"."request_entries" to "service_role";
|
||||||
|
|
||||||
|
grant insert on table "audit"."request_entries" to "service_role";
|
||||||
|
|
||||||
|
grant references on table "audit"."request_entries" to "service_role";
|
||||||
|
|
||||||
|
grant select on table "audit"."request_entries" to "service_role";
|
||||||
|
|
||||||
|
grant trigger on table "audit"."request_entries" to "service_role";
|
||||||
|
|
||||||
|
grant truncate on table "audit"."request_entries" to "service_role";
|
||||||
|
|
||||||
|
grant update on table "audit"."request_entries" to "service_role";
|
||||||
|
|
||||||
|
create policy "service_role_all"
|
||||||
|
on "audit"."request_entries"
|
||||||
|
as permissive
|
||||||
|
for all
|
||||||
|
to service_role
|
||||||
|
using (true);
|
||||||
|
|
||||||
|
create table "public"."connected_online_reservation" (
|
||||||
|
"id" bigint generated by default as identity not null,
|
||||||
|
"user_id" uuid not null,
|
||||||
|
"booking_code" text not null,
|
||||||
|
"service_id" bigint not null,
|
||||||
|
"clinic_id" bigint not null,
|
||||||
|
"service_user_id" bigint,
|
||||||
|
"sync_user_id" bigint not null,
|
||||||
|
"requires_payment" boolean not null,
|
||||||
|
"comments" text,
|
||||||
|
"start_time" timestamp with time zone not null,
|
||||||
|
"lang" text not null,
|
||||||
|
"discount_code" text,
|
||||||
|
"created_at" timestamp with time zone not null default now(),
|
||||||
|
"updated_at" timestamp with time zone default now()
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
alter table "public"."connected_online_reservation" enable row level security;
|
||||||
|
|
||||||
|
CREATE UNIQUE INDEX connected_online_reservation_booking_code_key ON public.connected_online_reservation USING btree (booking_code);
|
||||||
|
|
||||||
|
CREATE UNIQUE INDEX connected_online_reservation_pkey ON public.connected_online_reservation USING btree (id);
|
||||||
|
|
||||||
|
alter table "public"."connected_online_reservation" add constraint "connected_online_reservation_pkey" PRIMARY KEY using index "connected_online_reservation_pkey";
|
||||||
|
|
||||||
|
alter table "public"."connected_online_reservation" add constraint "connected_online_reservation_booking_code_key" UNIQUE using index "connected_online_reservation_booking_code_key";
|
||||||
|
|
||||||
|
alter table "public"."connected_online_reservation" add constraint "connected_online_reservation_user_id_fkey" FOREIGN KEY (user_id) REFERENCES auth.users(id) ON UPDATE CASCADE ON DELETE CASCADE not valid;
|
||||||
|
|
||||||
|
alter table "public"."connected_online_reservation" validate constraint "connected_online_reservation_user_id_fkey";
|
||||||
|
|
||||||
|
grant delete on table "public"."connected_online_reservation" to "service_role";
|
||||||
|
|
||||||
|
grant insert on table "public"."connected_online_reservation" to "service_role";
|
||||||
|
|
||||||
|
grant references on table "public"."connected_online_reservation" to "service_role";
|
||||||
|
|
||||||
|
grant select on table "public"."connected_online_reservation" to "service_role";
|
||||||
|
|
||||||
|
grant trigger on table "public"."connected_online_reservation" to "service_role";
|
||||||
|
|
||||||
|
grant truncate on table "public"."connected_online_reservation" to "service_role";
|
||||||
|
|
||||||
|
grant update on table "public"."connected_online_reservation" to "service_role";
|
||||||
|
|
||||||
|
create policy "service_role_all"
|
||||||
|
on "public"."connected_online_reservation"
|
||||||
|
as permissive
|
||||||
|
for all
|
||||||
|
to service_role
|
||||||
|
using (true);
|
||||||
|
|
||||||
|
|
||||||
|
CREATE TRIGGER connected_online_providers_change_record_timestamps AFTER INSERT OR UPDATE ON public.connected_online_providers FOR EACH ROW EXECUTE FUNCTION trigger_set_timestamps();
|
||||||
|
|
||||||
|
CREATE TRIGGER connected_online_services_change_record_timestamps AFTER INSERT OR UPDATE ON public.connected_online_services FOR EACH ROW EXECUTE FUNCTION trigger_set_timestamps();
|
||||||
|
|
||||||
|
create policy "service_role_all"
|
||||||
|
on "public"."connected_online_providers"
|
||||||
|
as permissive
|
||||||
|
for all
|
||||||
|
to service_role
|
||||||
|
using (true);
|
||||||
|
|
||||||
|
|
||||||
|
create policy "service_role_all"
|
||||||
|
on "public"."connected_online_services"
|
||||||
|
as permissive
|
||||||
|
for all
|
||||||
|
to service_role
|
||||||
|
using (true);
|
||||||
|
|
||||||
|
create policy "authenticated_select"
|
||||||
|
on "public"."connected_online_providers"
|
||||||
|
as permissive
|
||||||
|
for select
|
||||||
|
to authenticated
|
||||||
|
using (true);
|
||||||
|
|
||||||
|
create policy "authenticated_select"
|
||||||
|
on "public"."connected_online_services"
|
||||||
|
as permissive
|
||||||
|
for select
|
||||||
|
to authenticated
|
||||||
|
using (true);
|
||||||
|
|
||||||
|
|
||||||
|
create policy "own_all"
|
||||||
|
on "public"."connected_online_reservation"
|
||||||
|
as permissive
|
||||||
|
for all
|
||||||
|
to authenticated
|
||||||
|
using ((( SELECT auth.uid() AS uid) = user_id));
|
||||||
Reference in New Issue
Block a user