v1.0
This commit is contained in:
@@ -0,0 +1,22 @@
|
||||
import { desc, count } from "drizzle-orm";
|
||||
import { getDb } from "./connection";
|
||||
import { activityLogs } from "@db/schema";
|
||||
import type { InsertActivityLog } from "@db/schema";
|
||||
|
||||
export async function findRecentActivities(limit: number = 20) {
|
||||
const db = getDb();
|
||||
return db.select().from(activityLogs)
|
||||
.orderBy(desc(activityLogs.createdAt))
|
||||
.limit(limit);
|
||||
}
|
||||
|
||||
export async function createActivityLog(data: InsertActivityLog) {
|
||||
const db = getDb();
|
||||
await db.insert(activityLogs).values(data);
|
||||
}
|
||||
|
||||
export async function countActivities() {
|
||||
const db = getDb();
|
||||
const result = await db.select({ count: count() }).from(activityLogs);
|
||||
return result[0]?.count ?? 0;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import { drizzle } from "drizzle-orm/mysql2";
|
||||
import { env } from "../lib/env";
|
||||
import * as schema from "@db/schema";
|
||||
import * as relations from "@db/relations";
|
||||
|
||||
const fullSchema = { ...schema, ...relations };
|
||||
|
||||
let instance: ReturnType<typeof drizzle<typeof fullSchema>>;
|
||||
|
||||
export function getDb() {
|
||||
if (!instance) {
|
||||
instance = drizzle(env.databaseUrl, {
|
||||
mode: "planetscale",
|
||||
schema: fullSchema,
|
||||
});
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
import { sql, eq } from "drizzle-orm";
|
||||
import { getDb } from "./connection";
|
||||
import { recipients, placesOfWorship, distributions } from "@db/schema";
|
||||
|
||||
export async function getDashboardStats() {
|
||||
const db = getDb();
|
||||
|
||||
const totalRecipients = await db.select({ count: sql<number>`COUNT(*)` }).from(recipients);
|
||||
const activeRecipients = await db.select({ count: sql<number>`COUNT(*)` })
|
||||
.from(recipients)
|
||||
.where(eq(recipients.status, "active"));
|
||||
const pendingRecipients = await db.select({ count: sql<number>`COUNT(*)` })
|
||||
.from(recipients)
|
||||
.where(eq(recipients.status, "pending"));
|
||||
const totalPlaces = await db.select({ count: sql<number>`COUNT(*)` }).from(placesOfWorship);
|
||||
const activePlaces = await db.select({ count: sql<number>`COUNT(*)` })
|
||||
.from(placesOfWorship)
|
||||
.where(eq(placesOfWorship.isActive, "yes"));
|
||||
const totalDistributions = await db.select({ count: sql<number>`COUNT(*)` }).from(distributions);
|
||||
const totalAidAmount = await db.select({ total: sql<number>`COALESCE(SUM(amount), 0)` })
|
||||
.from(distributions);
|
||||
|
||||
const byType = await db.select({
|
||||
type: placesOfWorship.type,
|
||||
count: sql<number>`COUNT(*)`,
|
||||
}).from(placesOfWorship).groupBy(placesOfWorship.type);
|
||||
|
||||
const byStatus = await db.select({
|
||||
status: recipients.status,
|
||||
count: sql<number>`COUNT(*)`,
|
||||
}).from(recipients).groupBy(recipients.status);
|
||||
|
||||
const monthlyDist = await db.select({
|
||||
month: sql<string>`DATE_FORMAT(distribution_date, '%Y-%m')`,
|
||||
count: sql<number>`COUNT(*)`,
|
||||
amount: sql<number>`COALESCE(SUM(amount), 0)`,
|
||||
}).from(distributions)
|
||||
.groupBy(sql`DATE_FORMAT(distribution_date, '%Y-%m')`)
|
||||
.orderBy(sql`DATE_FORMAT(distribution_date, '%Y-%m')`)
|
||||
.limit(12);
|
||||
|
||||
return {
|
||||
totalRecipients: totalRecipients[0]?.count ?? 0,
|
||||
activeRecipients: activeRecipients[0]?.count ?? 0,
|
||||
pendingRecipients: pendingRecipients[0]?.count ?? 0,
|
||||
totalPlaces: totalPlaces[0]?.count ?? 0,
|
||||
activePlaces: activePlaces[0]?.count ?? 0,
|
||||
totalDistributions: totalDistributions[0]?.count ?? 0,
|
||||
totalAidAmount: Number(totalAidAmount[0]?.total ?? 0),
|
||||
byType,
|
||||
byStatus,
|
||||
monthlyDist,
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
import { eq, and, desc, count, sql } from "drizzle-orm";
|
||||
import { getDb } from "./connection";
|
||||
import { distributions } from "@db/schema";
|
||||
import type { InsertDistribution } from "@db/schema";
|
||||
|
||||
export async function findAllDistributions(placeOfWorshipId?: number, recipientId?: number) {
|
||||
const db = getDb();
|
||||
const conditions = [];
|
||||
if (placeOfWorshipId) conditions.push(eq(distributions.placeOfWorshipId, placeOfWorshipId));
|
||||
if (recipientId) conditions.push(eq(distributions.recipientId, recipientId));
|
||||
const where = conditions.length > 0 ? and(...conditions) : undefined;
|
||||
return db.select().from(distributions).where(where).orderBy(desc(distributions.distributionDate));
|
||||
}
|
||||
|
||||
export async function findDistributionById(id: number) {
|
||||
const db = getDb();
|
||||
const rows = await db.select().from(distributions).where(eq(distributions.id, id)).limit(1);
|
||||
return rows.at(0) ?? null;
|
||||
}
|
||||
|
||||
export async function createDistribution(data: InsertDistribution) {
|
||||
const db = getDb();
|
||||
const [{ id }] = await db.insert(distributions).values(data).$returningId();
|
||||
return findDistributionById(id);
|
||||
}
|
||||
|
||||
export async function deleteDistribution(id: number) {
|
||||
const db = getDb();
|
||||
await db.delete(distributions).where(eq(distributions.id, id));
|
||||
}
|
||||
|
||||
export async function countDistributions() {
|
||||
const db = getDb();
|
||||
const result = await db.select({ count: count() }).from(distributions);
|
||||
return result[0]?.count ?? 0;
|
||||
}
|
||||
|
||||
export async function sumDistributionAmount() {
|
||||
const db = getDb();
|
||||
const result = await db.select({ total: sql<number>`COALESCE(SUM(${distributions.amount}), 0)` })
|
||||
.from(distributions);
|
||||
return result[0]?.total ?? 0;
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
import { eq, like, and, count, desc } from "drizzle-orm";
|
||||
import { getDb } from "./connection";
|
||||
import { placesOfWorship } from "@db/schema";
|
||||
import type { InsertPlaceOfWorship } from "@db/schema";
|
||||
|
||||
export async function findAllPlaces(search?: string, type?: string, active?: string) {
|
||||
const db = getDb();
|
||||
const conditions = [];
|
||||
if (search) {
|
||||
conditions.push(like(placesOfWorship.name, `%${search}%`));
|
||||
}
|
||||
if (type && type !== "all") {
|
||||
conditions.push(eq(placesOfWorship.type, type as "mosque" | "church" | "temple" | "vihara" | "other"));
|
||||
}
|
||||
if (active && active !== "all") {
|
||||
conditions.push(eq(placesOfWorship.isActive, active as "yes" | "no"));
|
||||
}
|
||||
const where = conditions.length > 0 ? and(...conditions) : undefined;
|
||||
return db.select().from(placesOfWorship).where(where).orderBy(desc(placesOfWorship.createdAt));
|
||||
}
|
||||
|
||||
export async function findPlaceById(id: number) {
|
||||
const db = getDb();
|
||||
const rows = await db.select().from(placesOfWorship).where(eq(placesOfWorship.id, id)).limit(1);
|
||||
return rows.at(0) ?? null;
|
||||
}
|
||||
|
||||
export async function createPlace(data: InsertPlaceOfWorship) {
|
||||
const db = getDb();
|
||||
const [{ id }] = await db.insert(placesOfWorship).values(data).$returningId();
|
||||
return findPlaceById(id);
|
||||
}
|
||||
|
||||
export async function updatePlace(id: number, data: Partial<InsertPlaceOfWorship>) {
|
||||
const db = getDb();
|
||||
await db.update(placesOfWorship).set(data).where(eq(placesOfWorship.id, id));
|
||||
return findPlaceById(id);
|
||||
}
|
||||
|
||||
export async function deletePlace(id: number) {
|
||||
const db = getDb();
|
||||
await db.delete(placesOfWorship).where(eq(placesOfWorship.id, id));
|
||||
}
|
||||
|
||||
export async function countPlaces() {
|
||||
const db = getDb();
|
||||
const result = await db.select({ count: count() }).from(placesOfWorship);
|
||||
return result[0]?.count ?? 0;
|
||||
}
|
||||
|
||||
export async function countActivePlaces() {
|
||||
const db = getDb();
|
||||
const result = await db.select({ count: count() })
|
||||
.from(placesOfWorship)
|
||||
.where(eq(placesOfWorship.isActive, "yes"));
|
||||
return result[0]?.count ?? 0;
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
import { eq, like, and, count, desc } from "drizzle-orm";
|
||||
import { getDb } from "./connection";
|
||||
import { recipients } from "@db/schema";
|
||||
import type { InsertRecipient } from "@db/schema";
|
||||
|
||||
export async function findAllRecipients(
|
||||
search?: string,
|
||||
status?: string,
|
||||
placeOfWorshipId?: number
|
||||
) {
|
||||
const db = getDb();
|
||||
const conditions = [];
|
||||
if (search) {
|
||||
conditions.push(like(recipients.name, `%${search}%`));
|
||||
}
|
||||
if (status && status !== "all") {
|
||||
conditions.push(eq(recipients.status, status as "pending" | "active" | "rejected" | "suspended"));
|
||||
}
|
||||
if (placeOfWorshipId) {
|
||||
conditions.push(eq(recipients.placeOfWorshipId, placeOfWorshipId));
|
||||
}
|
||||
const where = conditions.length > 0 ? and(...conditions) : undefined;
|
||||
return db.select().from(recipients).where(where).orderBy(desc(recipients.createdAt));
|
||||
}
|
||||
|
||||
export async function findRecipientById(id: number) {
|
||||
const db = getDb();
|
||||
const rows = await db.select().from(recipients).where(eq(recipients.id, id)).limit(1);
|
||||
return rows.at(0) ?? null;
|
||||
}
|
||||
|
||||
export async function findPendingRecipients(placeOfWorshipId?: number) {
|
||||
const db = getDb();
|
||||
const conditions = [eq(recipients.status, "pending")];
|
||||
if (placeOfWorshipId) {
|
||||
conditions.push(eq(recipients.placeOfWorshipId, placeOfWorshipId));
|
||||
}
|
||||
return db.select().from(recipients)
|
||||
.where(and(...conditions))
|
||||
.orderBy(desc(recipients.createdAt));
|
||||
}
|
||||
|
||||
export async function createRecipient(data: InsertRecipient) {
|
||||
const db = getDb();
|
||||
const [{ id }] = await db.insert(recipients).values(data).$returningId();
|
||||
return findRecipientById(id);
|
||||
}
|
||||
|
||||
export async function updateRecipient(id: number, data: Partial<InsertRecipient>) {
|
||||
const db = getDb();
|
||||
await db.update(recipients).set(data).where(eq(recipients.id, id));
|
||||
return findRecipientById(id);
|
||||
}
|
||||
|
||||
export async function verifyRecipient(
|
||||
id: number,
|
||||
status: "active" | "rejected",
|
||||
verifiedBy: number,
|
||||
rejectionReason?: string
|
||||
) {
|
||||
return updateRecipient(id, {
|
||||
status,
|
||||
verifiedBy,
|
||||
verifiedAt: new Date(),
|
||||
rejectionReason: status === "rejected" ? rejectionReason : null,
|
||||
});
|
||||
}
|
||||
|
||||
export async function deleteRecipient(id: number) {
|
||||
const db = getDb();
|
||||
await db.delete(recipients).where(eq(recipients.id, id));
|
||||
}
|
||||
|
||||
export async function countRecipients(status?: string) {
|
||||
const db = getDb();
|
||||
const query = db.select({ count: count() }).from(recipients);
|
||||
if (status) {
|
||||
return query.where(eq(recipients.status, status as "pending" | "active" | "rejected" | "suspended"));
|
||||
}
|
||||
const result = await query;
|
||||
return result[0]?.count ?? 0;
|
||||
}
|
||||
|
||||
export async function countActiveRecipients() {
|
||||
return countRecipients("active");
|
||||
}
|
||||
|
||||
export async function countPendingRecipients() {
|
||||
return countRecipients("pending");
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
import { eq, or } from "drizzle-orm";
|
||||
import * as schema from "@db/schema";
|
||||
import type { InsertUser } from "@db/schema";
|
||||
import { getDb } from "./connection";
|
||||
import { env } from "../lib/env";
|
||||
|
||||
export async function findUserByUnionId(unionId: string) {
|
||||
const rows = await getDb()
|
||||
.select()
|
||||
.from(schema.users)
|
||||
.where(eq(schema.users.unionId, unionId))
|
||||
.limit(1);
|
||||
return rows.at(0);
|
||||
}
|
||||
|
||||
export async function findUserByLoginIdentifier(identifier: string) {
|
||||
const rows = await getDb()
|
||||
.select()
|
||||
.from(schema.users)
|
||||
.where(or(eq(schema.users.unionId, identifier), eq(schema.users.email, identifier)))
|
||||
.limit(1);
|
||||
return rows.at(0);
|
||||
}
|
||||
|
||||
export async function touchUserLastSignIn(unionId: string) {
|
||||
await getDb()
|
||||
.update(schema.users)
|
||||
.set({ lastSignInAt: new Date() })
|
||||
.where(eq(schema.users.unionId, unionId));
|
||||
}
|
||||
|
||||
export async function upsertUser(data: InsertUser) {
|
||||
const values = { ...data };
|
||||
const updateSet: Partial<InsertUser> = {
|
||||
lastSignInAt: new Date(),
|
||||
...data,
|
||||
};
|
||||
|
||||
if (
|
||||
values.role === undefined &&
|
||||
values.unionId &&
|
||||
values.unionId === env.ownerUnionId
|
||||
) {
|
||||
values.role = "admin";
|
||||
updateSet.role = "admin";
|
||||
}
|
||||
|
||||
await getDb()
|
||||
.insert(schema.users)
|
||||
.values(values)
|
||||
.onDuplicateKeyUpdate({ set: updateSet });
|
||||
}
|
||||
Reference in New Issue
Block a user