Complete rewrite of all of the hooks and actions for statuses as well as for the Tech Table. Need to rewrite history component & then I will be happy & ready to continue

This commit is contained in:
2025-06-13 04:26:52 -05:00
parent 653fe64bbf
commit c96bdab91b
9 changed files with 718 additions and 928 deletions

View File

@ -1,142 +0,0 @@
'use server';
import 'server-only';
import { createServerClient } from '@/utils/supabase';
import type { Result } from '.';
import type { Profile } from '@/utils/supabase';
export type UserStatus = Profile & {
status: string;
created_at: string;
updated_by?: Profile;
}
export const getRecentUsers = async (): Promise<Result<string[]>> => {
try {
const supabase = await createServerClient();
// Get users who have had status updates in the last week
const oneWeekAgo = new Date();
oneWeekAgo.setDate(oneWeekAgo.getDate() - 7);
const { data, error } = await supabase
.from('statuses')
.select('user_id')
.gte('created_at', oneWeekAgo.toISOString())
.order('created_at', { ascending: false });
if (error) throw error;
// Get unique user IDs
const uniqueUserIds = [...new Set(data.map(status => status.user_id))];
return { success: true, data: uniqueUserIds };
} catch (error) {
return {
success: false,
error:
error instanceof Error
? error.message
: 'Unknown error getting recent users',
};
}
};
export const getUserStatuses = async (): Promise<Result<UserStatus[]>> => {
try {
const supabase = await createServerClient();
// First get the recent users
const recentUsersResult = await getRecentUsers();
if (!recentUsersResult.success) {
throw new Error(recentUsersResult.error);
}
const userIds = recentUsersResult.data;
if (userIds.length === 0) {
return { success: true, data: [] };
}
// Get the most recent status for each user
const { data: statusData, error: statusError } = await supabase
.from('statuses')
.select('user_id, status, created_at, updated_by_id')
.in('user_id', userIds)
.order('created_at', { ascending: false });
if (statusError) throw statusError;
if (!statusData) {
return { success: true, data: [] };
}
// Group by user_id and get the most recent status for each user
const userStatusMap = new Map<string, typeof statusData[0]>();
statusData.forEach(status => {
if (!userStatusMap.has(status.user_id)) {
userStatusMap.set(status.user_id, status);
}
});
// Get all unique user IDs from the status data
const statusUserIds = Array.from(userStatusMap.keys());
// Get profile information for these users
const { data: profileData, error: profileError } = await supabase
.from('profiles')
.select('id, full_name, email, avatar_url, provider, updated_at')
.in('id', statusUserIds);
if (profileError) throw profileError;
// Get updated_by profile information
const updatedByIds = Array.from(userStatusMap.values())
.map(status => status.updated_by_id)
.filter((id): id is string => id !== null);
const { data: updatedByData, error: updatedByError } = await supabase
.from('profiles')
.select('id, full_name, email, avatar_url, provider, updated_at')
.in('id', updatedByIds);
if (updatedByError) throw updatedByError;
// Create maps for easy lookup
const profileMap = new Map(profileData?.map(profile => [profile.id, profile]) ?? []);
const updatedByMap = new Map(updatedByData?.map(profile => [profile.id, profile]) ?? []);
// Transform the data to match UserStatus type
const userStatuses: UserStatus[] = [];
for (const status of userStatusMap.values()) {
const profile = profileMap.get(status.user_id);
const updatedBy = status.updated_by_id ? updatedByMap.get(status.updated_by_id) : undefined;
if (!profile) continue; // Skip if no profile found
userStatuses.push({
// Profile fields
id: profile.id,
full_name: profile.full_name,
email: profile.email,
avatar_url: profile.avatar_url,
provider: profile.provider,
updated_at: profile.updated_at,
// Status fields
status: status.status,
created_at: status.created_at,
updated_by: updatedBy,
});
}
return { success: true, data: userStatuses };
} catch (error) {
return {
success: false,
error:
error instanceof Error
? error.message
: 'Unknown error getting user statuses',
};
}
};

View File

@ -1,21 +1,18 @@
'use server'
import 'server-only';
import {
createServerClient,
type Profile,
type Result,
type Status,
} from '@/utils/supabase';
'use server';
import { createServerClient } from '@/utils/supabase';
import type { Profile, Result, Status } from '@/utils/supabase';
import { getUser, getProfile } from '@/lib/hooks'
type UserWithStatus = {
user: Profile;
status: string;
created_at: string;
updated_by: Profile;
updated_by?: Profile;
};
type PaginatedHistory = UserWithStatus[] & {
type PaginatedHistory = {
profile?: Profile;
statuses: Status[];
meta: {
current_page: number;
per_page: number;
@ -24,15 +21,14 @@ type PaginatedHistory = UserWithStatus[] & {
};
};
export const getUsersWithStatuses = async () => {
export const getRecentUsersWithStatuses = async (): Promise<
Result<UserWithStatus[]>
> => {
try {
const supabase = await createServerClient();
const oneDayAgo = new Date(Date.now() - 1000 * 60 * 60 * 24);
// Get only users with recent statuses (Past 7 days)
const oneWeekAgo = new Date();
oneWeekAgo.setDate(oneWeekAgo.getDate() - 7);
const { data: recentStatuses, error } = await supabase
const { data, error } = await supabase
.from('statuses')
.select(`
user:profiles!user_id(*),
@ -40,14 +36,245 @@ export const getUsersWithStatuses = async () => {
created_at,
updated_by:profiles!updated_by_id(*)
`)
.gte('created_at', oneWeekAgo.toISOString())
.order('created_at', { ascending: false });
.gte('created_at', oneDayAgo.toISOString())
.order('created_at', { ascending: false }) as { data: UserWithStatus[], error: unknown };
if (error) throw error;
if (!recentStatuses.length) return { success: true, data: []};
if (error) throw error as Error;
if (!data?.length) return { success: true, data: [] };
return { success: true, data: recentStatuses };
// 3⃣ client-side dedupe: keep the first status you see per user
const seen = new Set<string>();
const filtered = data.filter((row) => {
if (seen.has(row.user.id)) return false;
seen.add(row.user.id);
return true;
});
return { success: true, data: filtered };
} catch (error) {
return { success: false, error: `Error: ${error as string}` };
return { success: false, error: `Error: ${error as Error}` };
}
};
export const broadcastStatusUpdates = async (
userStatuses: UserWithStatus[],
): Promise<Result<void>> => {
try {
const supabase = await createServerClient();
for (const userStatus of userStatuses) {
const broadcast = await supabase.channel('status_updates').send({
type: 'broadcast',
event: 'status_updated',
payload: {
user_status: userStatus,
timestamp: new Date().toISOString(),
}
});
if (broadcast === 'error' || broadcast === 'timed out')
throw new Error('Failed to broadcast status update. Timed out or errored.');
}
return { success: true, data: undefined };
} catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : 'Unknown error',
};
}
};
export const updateStatuses = async (
userIds: string[],
status: string,
): Promise<Result<void>> => {
try {
const supabase = await createServerClient();
const userResponse = await getUser();
if (!userResponse.success) throw new Error('Not authenticated!');
const profileResponse = await getProfile();
if (!profileResponse.success) throw new Error(profileResponse.error);
const user = userResponse.data;
const userProfile = profileResponse.data;
const inserts = userIds.map(usersId => ({
user_id: usersId,
status,
updated_by_id: user.id,
}));
const { data: insertedStatuses, error: insertedStatusesError } = await supabase
.from('statuses')
.insert(inserts)
.select();
if (insertedStatusesError) throw insertedStatusesError as Error;
if (insertedStatuses) {
const broadcastArray = new Array<UserWithStatus>(insertedStatuses.length);
for (const insertedStatus of insertedStatuses) {
const { data: profile, error: profileError } = await supabase
.from('profiles')
.select('*')
.eq('id', insertedStatus.user_id)
.single();
if (profileError) throw profileError as Error;
if (profile) {
broadcastArray.push({
user: profile,
status: insertedStatus.status,
created_at: insertedStatus.created_at,
updated_by: userProfile,
});
}
}
await broadcastStatusUpdates(broadcastArray);
}
return { success: true, data: undefined };
} catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : 'Unknown error',
};
}
};
export const updateUserStatus = async (status: string): Promise<Result<void>> => {
try {
const supabase = await createServerClient();
const userResponse = await getUser();
if (!userResponse.success) throw new Error(`Not authenticated! ${userResponse.error}`);
const profileResponse = await getProfile();
if (!profileResponse.success) throw new Error(`Could not get profile! ${profileResponse.error}`);
const user = userResponse.data;
const userProfile = profileResponse.data;
const { data: insertedStatus, error: insertedStatusError } = await supabase
.from('statuses')
.insert({
user_id: user.id,
status,
updated_by_id: user.id,
})
.select()
.single();
if (insertedStatusError) throw insertedStatusError as Error;
const userStatus: UserWithStatus = {
user: userProfile,
status: insertedStatus.status,
created_at: insertedStatus.created_at,
};
await broadcastStatusUpdates([userStatus]);
return { success: true, data: undefined };
} catch (error) {
return {
success: false,
error: `Error updating user's status: ${error as Error}`,
}
}
};
export const getUserHistory = async (
userId: string,
page = 1,
perPage = 50,
): Promise<Result<PaginatedHistory>> => {
try {
const supabase = await createServerClient();
const userResponse = await getUser();
if (!userResponse.success) throw new Error(`Not authenticated! ${userResponse.error}`);
const offset = (page - 1) * perPage;
const { count } = await supabase
.from('statuses')
.select('*', { count: 'exact', head: true })
.eq('user_id', userId);
const { data: statuses, error: statusesError } = await supabase
.from('statuses')
.select('*')
.eq('user_id', userId)
.order('created_at', { ascending: false })
.range(offset, offset + perPage - 1) as {data: Status[], error: unknown};
if (statusesError) throw statusesError as Error;
const { data: profile, error: profileError } = await supabase
.from('profiles')
.select('*')
.eq('id', userId)
.single() as { data: Profile, error: unknown };
if (profileError) throw profileError as Error;
if (!profile) throw new Error('User profile not found!');
const totalCount = count ?? 0;
const totalPages = Math.ceil(totalCount / perPage);
return {
success: true,
data: {
profile,
statuses,
meta: {
current_page: page,
per_page: perPage,
total_pages: totalPages,
total_count: totalCount,
},
},
};
} catch (error) {
return {
success: false,
error: `Error getting user's history: ${error as Error}`,
};
}
};
export const getAllHistory = async (
page = 1,
perPage = 50,
): Promise<Result<PaginatedHistory>> => {
try {
const supabase = await createServerClient();
const userResponse = await getUser();
if (!userResponse.success) throw new Error(`Not authenticated! ${userResponse.error}`);
const offset = (page - 1) * perPage;
const { count } = await supabase
.from('statuses')
.select('*', { count: 'exact', head: true });
const { data: statuses, error: statusesError } = await supabase
.from('statuses')
.select('*')
.order('created_at', { ascending: false })
.range(offset, offset + perPage - 1) as {data: Status[], error: unknown};
if (statusesError) throw statusesError as Error;
const totalCount = count ?? 0;
const totalPages = Math.ceil(totalCount / perPage);
return {
success: true,
data: {
statuses,
meta: {
current_page: page,
per_page: perPage,
total_pages: totalPages,
total_count: totalCount,
},
},
};
} catch (error) {
return {
success: false,
error: `Error getting all history: ${error as Error}`,
};
}
};

View File

@ -1,401 +0,0 @@
'use client';
import { createClient } from '@/utils/supabase';
import type { Result } from '.';
import type { Profile, Status } from '@/utils/supabase';
export type UserStatus = Profile & {
status: string;
created_at: string;
updated_by?: Profile;
};
export type HistoryEntry = {
id: string;
status: string;
created_at: string;
updated_by?: Profile;
user_profile: Profile;
};
export type PaginatedHistory = {
data: HistoryEntry[];
meta: {
current_page: number;
per_page: number;
total_pages: number;
total_count: number;
};
};
export const getUserStatuses = async (): Promise<Result<UserStatus[]>> => {
try {
const supabase = createClient();
// Get users with recent activity (last 7 days)
const oneWeekAgo = new Date();
oneWeekAgo.setDate(oneWeekAgo.getDate() - 7);
const { data: recentStatuses, error: statusError } = await supabase
.from('statuses')
.select('*')
.gte('created_at', oneWeekAgo.toISOString())
.order('created_at', { ascending: false });
if (statusError) throw statusError;
if (!recentStatuses?.length) return { success: true, data: [] };
// Properly type the status data
const typedStatuses: Status[] = recentStatuses;
// Get most recent status per user
const userStatusMap = new Map<string, Status>();
typedStatuses.forEach(status => {
if (!userStatusMap.has(status.user_id)) {
userStatusMap.set(status.user_id, status);
}
});
const userIds = Array.from(userStatusMap.keys());
// Get profiles
const { data: profiles, error: profileError } = await supabase
.from('profiles')
.select('*')
.in('id', userIds);
if (profileError) throw profileError;
// Get updated_by profiles - filter out nulls properly
const updatedByIds = Array.from(userStatusMap.values())
.map(s => s.updated_by_id)
.filter((id): id is string => id !== null);
let updatedByProfiles: Profile[] = [];
if (updatedByIds.length > 0) {
const { data, error } = await supabase
.from('profiles')
.select('*')
.in('id', updatedByIds);
if (error) throw error;
updatedByProfiles = data ?? [];
}
const profileMap = new Map((profiles ?? []).map(p => [p.id, p]));
const updatedByMap = new Map(updatedByProfiles.map(p => [p.id, p]));
const userStatuses: UserStatus[] = [];
for (const [userId, status] of userStatusMap) {
const profile = profileMap.get(userId);
if (!profile) continue;
userStatuses.push({
...profile,
status: status.status,
created_at: status.created_at,
updated_by: status.updated_by_id ? updatedByMap.get(status.updated_by_id) : undefined,
});
}
return { success: true, data: userStatuses };
} catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : 'Unknown error',
};
}
};
export const broadcastStatusUpdate = async (
userStatus: UserStatus
): Promise<Result<void>> => {
try {
const supabase = createClient();
const broadcast = await supabase.channel('status_updates').send({
type: 'broadcast',
event: 'status_updated',
payload: {
user_status: userStatus,
timestamp: new Date().toISOString(),
}
});
if (broadcast === 'error') throw new Error('Failed to broadcast status update');
if (broadcast === 'ok') return { success: true, data: undefined };
else throw new Error('Broadcast timed out!')
} catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : 'Unknown error',
};
}
};
// Update your existing functions to broadcast after database updates
export const updateUserStatus = async (
userIds: string[],
status: string,
): Promise<Result<void>> => {
try {
const supabase = createClient();
const { data: { user } } = await supabase.auth.getUser();
if (!user) throw new Error('Not authenticated');
const inserts = userIds.map(userId => ({
user_id: userId,
status,
updated_by_id: user.id,
}));
const { data: insertedStatuses, error } = await supabase
.from('statuses')
.insert(inserts)
.select();
if (error) throw error;
// Broadcast the updates
if (insertedStatuses) {
for (const insertedStatus of insertedStatuses) {
// Get the user profile for broadcasting
const { data: profile } = await supabase
.from('profiles')
.select('*')
.eq('id', insertedStatus.user_id)
.single();
if (profile) {
const userStatus: UserStatus = {
...profile,
status: insertedStatus.status,
created_at: insertedStatus.created_at,
};
await broadcastStatusUpdate(userStatus);
}
}
}
return { success: true, data: undefined };
} catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : 'Unknown error',
};
}
};
export const updateCurrentUserStatus = async (
status: string,
): Promise<Result<void>> => {
try {
const supabase = createClient();
const { data: { user } } = await supabase.auth.getUser();
if (!user) throw new Error('Not authenticated');
const { data: insertedStatus, error } = await supabase
.from('statuses')
.insert({
user_id: user.id,
status,
updated_by_id: user.id,
})
.select()
.single();
if (error) throw error;
// Get the user profile for broadcasting
const { data: profile } = await supabase
.from('profiles')
.select('*')
.eq('id', user.id)
.single();
if (profile && insertedStatus) {
const userStatus: UserStatus = {
...profile,
status: insertedStatus.status,
created_at: insertedStatus.created_at,
};
await broadcastStatusUpdate(userStatus);
}
return { success: true, data: undefined };
} catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : 'Unknown error',
};
}
};
export const getUserHistory = async (
userId: string,
page = 1,
perPage = 50,
): Promise<Result<PaginatedHistory>> => {
try {
const supabase = createClient();
const offset = (page - 1) * perPage;
// Get count
const { count } = await supabase
.from('statuses')
.select('*', { count: 'exact', head: true })
.eq('user_id', userId);
// Get data
const { data: statuses, error } = await supabase
.from('statuses')
.select('*')
.eq('user_id', userId)
.order('created_at', { ascending: false })
.range(offset, offset + perPage - 1);
if (error) throw error;
const typedStatuses: Status[] = statuses ?? [];
// Get user profile
const { data: userProfile, error: userProfileError } = await supabase
.from('profiles')
.select('*')
.eq('id', userId)
.single();
if (userProfileError) throw userProfileError;
if (!userProfile) throw new Error('User profile not found');
// Get updated_by profiles - filter out nulls properly
const updatedByIds = typedStatuses
.map(s => s.updated_by_id)
.filter((id): id is string => id !== null);
let updatedByProfiles: Profile[] = [];
if (updatedByIds.length > 0) {
const { data, error: updatedByError } = await supabase
.from('profiles')
.select('*')
.in('id', updatedByIds);
if (updatedByError) throw updatedByError;
updatedByProfiles = data ?? [];
}
const updatedByMap = new Map(updatedByProfiles.map(p => [p.id, p]));
const historyEntries: HistoryEntry[] = typedStatuses.map(entry => ({
id: entry.id,
status: entry.status,
created_at: entry.created_at,
updated_by: entry.updated_by_id ? updatedByMap.get(entry.updated_by_id) : undefined,
user_profile: userProfile,
}));
const totalCount = count ?? 0;
const totalPages = Math.ceil(totalCount / perPage);
return {
success: true,
data: {
data: historyEntries,
meta: {
current_page: page,
per_page: perPage,
total_pages: totalPages,
total_count: totalCount
},
},
};
} catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : 'Unknown error',
};
}
};
export const getAllHistory = async (
page = 1,
perPage = 50,
): Promise<Result<PaginatedHistory>> => {
try {
const supabase = createClient();
const offset = (page - 1) * perPage;
// Get count
const { count } = await supabase
.from('statuses')
.select('*', { count: 'exact', head: true });
// Get data
const { data: statuses, error } = await supabase
.from('statuses')
.select('*')
.order('created_at', { ascending: false })
.range(offset, offset + perPage - 1);
if (error) throw error;
const typedStatuses: Status[] = statuses ?? [];
// Get all profiles - filter out nulls properly
const userIds = [...new Set(typedStatuses.map(s => s.user_id))];
const updatedByIds = typedStatuses
.map(s => s.updated_by_id)
.filter((id): id is string => id !== null);
const allIds = [...new Set([...userIds, ...updatedByIds])];
const { data: profiles, error: profileError } = await supabase
.from('profiles')
.select('*')
.in('id', allIds);
if (profileError) throw profileError;
const profileMap = new Map((profiles ?? []).map(p => [p.id, p]));
const historyEntries: HistoryEntry[] = typedStatuses.map(entry => {
const userProfile = profileMap.get(entry.user_id);
if (!userProfile) {
throw new Error(`User profile not found for ID: ${entry.user_id}`);
}
return {
id: entry.id,
status: entry.status,
created_at: entry.created_at,
updated_by: entry.updated_by_id ? profileMap.get(entry.updated_by_id) : undefined,
user_profile: userProfile,
};
});
const totalCount = count ?? 0;
const totalPages = Math.ceil(totalCount / perPage);
return {
success: true,
data: {
data: historyEntries,
meta: {
current_page: page,
per_page: perPage,
total_pages: totalPages,
total_count: totalCount
},
},
};
} catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : 'Unknown error',
};
}
};

View File

@ -146,6 +146,6 @@ export const getUser = async (): Promise<Result<User>> => {
if (error) throw error;
return { success: true, data: data.user };
} catch (error) {
return { success: false, error: 'Could not get user!' };
return { success: false, error: `Could not get user! Error: ${error as Error}` };
}
};

View File

@ -1,20 +1,18 @@
'use client';
import { createClient } from '@/utils/supabase';
import type { Profile, Result, Status } from '@/utils/supabase';
import { getUser, getProfile } from '@/lib/hooks'
import {
createClient,
type Profile,
type Result,
type Status,
} from '@/utils/supabase';
type UserWithStatus = {
export type UserWithStatus = {
user: Profile;
status: string;
created_at: string;
updated_by: Profile;
updated_by?: Profile;
};
type PaginatedHistory = UserWithStatus[] & {
type PaginatedHistory = {
profile?: Profile;
statuses: Status[];
meta: {
current_page: number;
per_page: number;
@ -23,15 +21,14 @@ type PaginatedHistory = UserWithStatus[] & {
};
};
export const getUsersWithStatuses = async (): Promise<Result<UserWithStatus[]>> => {
export const getRecentUsersWithStatuses = async (): Promise<
Result<UserWithStatus[]>
> => {
try {
const supabase = createClient();
const oneDayAgo = new Date(Date.now() - 1000 * 60 * 60 * 24);
// Get only users with recent statuses (Past 7 days)
const oneWeekAgo = new Date();
oneWeekAgo.setDate(oneWeekAgo.getDate() - 7);
const { data: recentStatuses, error } = await supabase
const { data, error } = await supabase
.from('statuses')
.select(`
user:profiles!user_id(*),
@ -39,14 +36,246 @@ export const getUsersWithStatuses = async (): Promise<Result<UserWithStatus[]>>
created_at,
updated_by:profiles!updated_by_id(*)
`)
.gte('created_at', oneWeekAgo.toISOString())
.order('created_at', { ascending: false }) as {data: UserWithStatus[], error: unknown};
.gte('created_at', oneDayAgo.toISOString())
.order('created_at', { ascending: false }) as { data: UserWithStatus[], error: unknown };
if (error) throw error;
if (!recentStatuses.length) return { success: true, data: []};
if (error) throw error as Error;
if (!data?.length) return { success: true, data: [] };
return { success: true, data: recentStatuses };
// 3⃣ client-side dedupe: keep the first status you see per user
const seen = new Set<string>();
const filtered = data.filter((row) => {
if (seen.has(row.user.id)) return false;
seen.add(row.user.id);
return true;
});
return { success: true, data: filtered };
} catch (error) {
return { success: false, error: `Error: ${error as string}` };
return { success: false, error: `Error: ${error as Error}` };
}
};
export const broadcastStatusUpdates = async (
userStatuses: UserWithStatus[],
): Promise<Result<void>> => {
try {
const supabase = createClient();
for (const userStatus of userStatuses) {
const broadcast = await supabase.channel('status_updates').send({
type: 'broadcast',
event: 'status_updated',
payload: {
user_status: userStatus,
timestamp: new Date().toISOString(),
}
});
if (broadcast === 'error' || broadcast === 'timed out')
throw new Error('Failed to broadcast status update. Timed out or errored.');
}
return { success: true, data: undefined };
} catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : 'Unknown error',
};
}
};
export const updateStatuses = async (
userIds: string[],
status: string,
): Promise<Result<void>> => {
try {
const supabase = createClient();
const userResponse = await getUser();
if (!userResponse.success) throw new Error('Not authenticated!');
const profileResponse = await getProfile();
if (!profileResponse.success) throw new Error(profileResponse.error);
const user = userResponse.data;
const userProfile = profileResponse.data;
const inserts = userIds.map(usersId => ({
user_id: usersId,
status,
updated_by_id: user.id,
}));
const { data: insertedStatuses, error: insertedStatusesError } = await supabase
.from('statuses')
.insert(inserts)
.select();
if (insertedStatusesError) throw insertedStatusesError as Error;
if (insertedStatuses) {
const broadcastArray = new Array<UserWithStatus>(insertedStatuses.length);
for (const insertedStatus of insertedStatuses) {
const { data: profile, error: profileError } = await supabase
.from('profiles')
.select('*')
.eq('id', insertedStatus.user_id)
.single();
if (profileError) throw profileError as Error;
if (profile) {
broadcastArray.push({
user: profile,
status: insertedStatus.status,
created_at: insertedStatus.created_at,
updated_by: userProfile,
});
}
}
await broadcastStatusUpdates(broadcastArray);
}
return { success: true, data: undefined };
} catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : 'Unknown error',
};
}
};
export const updateUserStatus = async (status: string): Promise<Result<void>> => {
try {
const supabase = createClient();
const userResponse = await getUser();
if (!userResponse.success) throw new Error(`Not authenticated! ${userResponse.error}`);
const profileResponse = await getProfile();
if (!profileResponse.success) throw new Error(`Could not get profile! ${profileResponse.error}`);
const user = userResponse.data;
const userProfile = profileResponse.data;
const { data: insertedStatus, error: insertedStatusError } = await supabase
.from('statuses')
.insert({
user_id: user.id,
status,
updated_by_id: user.id,
})
.select()
.single();
if (insertedStatusError) throw insertedStatusError as Error;
const userStatus: UserWithStatus = {
user: userProfile,
status: insertedStatus.status,
created_at: insertedStatus.created_at,
};
await broadcastStatusUpdates([userStatus]);
return { success: true, data: undefined };
} catch (error) {
return {
success: false,
error: `Error updating user's status: ${error as Error}`,
}
}
};
export const getUserHistory = async (
userId: string,
page = 1,
perPage = 50,
): Promise<Result<PaginatedHistory>> => {
try {
const supabase = createClient();
const userResponse = await getUser();
if (!userResponse.success) throw new Error(`Not authenticated! ${userResponse.error}`);
const offset = (page - 1) * perPage;
const { count } = await supabase
.from('statuses')
.select('*', { count: 'exact', head: true })
.eq('user_id', userId);
const { data: statuses, error: statusesError } = await supabase
.from('statuses')
.select('*')
.eq('user_id', userId)
.order('created_at', { ascending: false })
.range(offset, offset + perPage - 1) as {data: Status[], error: unknown};
if (statusesError) throw statusesError as Error;
const { data: profile, error: profileError } = await supabase
.from('profiles')
.select('*')
.eq('id', userId)
.single() as { data: Profile, error: unknown };
if (profileError) throw profileError as Error;
if (!profile) throw new Error('User profile not found!');
const totalCount = count ?? 0;
const totalPages = Math.ceil(totalCount / perPage);
return {
success: true,
data: {
profile,
statuses,
meta: {
current_page: page,
per_page: perPage,
total_pages: totalPages,
total_count: totalCount,
},
},
};
} catch (error) {
return {
success: false,
error: `Error getting user's history: ${error as Error}`,
};
}
};
export const getAllHistory = async (
page = 1,
perPage = 50,
): Promise<Result<PaginatedHistory>> => {
try {
const supabase = createClient();
const userResponse = await getUser();
if (!userResponse.success) throw new Error(`Not authenticated! ${userResponse.error}`);
const offset = (page - 1) * perPage;
const { count } = await supabase
.from('statuses')
.select('*', { count: 'exact', head: true });
const { data: statuses, error: statusesError } = await supabase
.from('statuses')
.select('*')
.order('created_at', { ascending: false })
.range(offset, offset + perPage - 1) as {data: Status[], error: unknown};
if (statusesError) throw statusesError as Error;
const totalCount = count ?? 0;
const totalPages = Math.ceil(totalCount / perPage);
return {
success: true,
data: {
statuses,
meta: {
current_page: page,
per_page: perPage,
total_pages: totalPages,
total_count: totalCount,
},
},
};
} catch (error) {
return {
success: false,
error: `Error getting all history: ${error as Error}`,
}
}
};