- Add create/close post mutations with auth & ownership verification - Add getOrCreate chat mutation for handyman response flow - PostCard component: title, description preview, category badge, location, budget, status badge (open=green, closed=red) - Post detail screen: full description, category, location, budget, status, time-ago. Owner: close button. Handyman: respond button creates/opens chat with customer - Create post screen: title, description, category picker, location, budget, auth-gated with redirect - Posts feed: open/all filter tabs, new-post button for customers All UI text in Macedonian. TypeScript clean, Convex functions deployed.
91 lines
2.6 KiB
TypeScript
91 lines
2.6 KiB
TypeScript
import { query, mutation } from "./_generated/server";
|
||
import { v } from "convex/values";
|
||
|
||
export const list = query({
|
||
args: { status: v.optional(v.union(v.literal("open"), v.literal("closed"))) },
|
||
handler: async (ctx, args) => {
|
||
if (args.status) {
|
||
return await ctx.db
|
||
.query("posts")
|
||
.withIndex("by_status", (q) => q.eq("status", args.status!))
|
||
.order("desc")
|
||
.collect();
|
||
}
|
||
return await ctx.db.query("posts").order("desc").collect();
|
||
},
|
||
});
|
||
|
||
export const getByCustomer = query({
|
||
args: { customerId: v.id("users") },
|
||
handler: async (ctx, args) => {
|
||
return await ctx.db
|
||
.query("posts")
|
||
.withIndex("by_customer", (q) => q.eq("customerId", args.customerId))
|
||
.order("desc")
|
||
.collect();
|
||
},
|
||
});
|
||
|
||
export const getById = query({
|
||
args: { id: v.id("posts") },
|
||
handler: async (ctx, args) => {
|
||
return await ctx.db.get(args.id);
|
||
},
|
||
});
|
||
|
||
export const create = mutation({
|
||
args: {
|
||
token: v.string(),
|
||
title: v.string(),
|
||
description: v.string(),
|
||
category: v.optional(v.string()),
|
||
location: v.optional(v.string()),
|
||
budget: v.optional(v.string()),
|
||
},
|
||
handler: async (ctx, args) => {
|
||
const session = await ctx.db
|
||
.query("sessions")
|
||
.withIndex("by_token", (q) => q.eq("token", args.token))
|
||
.first();
|
||
if (!session) throw new Error("Неавторизиран");
|
||
|
||
const user = await ctx.db.get(session.userId);
|
||
if (!user) throw new Error("Корисникот не е пронајден");
|
||
|
||
const now = Date.now();
|
||
const postId = await ctx.db.insert("posts", {
|
||
customerId: session.userId,
|
||
title: args.title,
|
||
description: args.description,
|
||
category: args.category,
|
||
location: args.location,
|
||
budget: args.budget,
|
||
status: "open",
|
||
createdAt: now,
|
||
updatedAt: now,
|
||
});
|
||
|
||
return postId;
|
||
},
|
||
});
|
||
|
||
export const close = mutation({
|
||
args: {
|
||
token: v.string(),
|
||
postId: v.id("posts"),
|
||
},
|
||
handler: async (ctx, args) => {
|
||
const session = await ctx.db
|
||
.query("sessions")
|
||
.withIndex("by_token", (q) => q.eq("token", args.token))
|
||
.first();
|
||
if (!session) throw new Error("Неавторизиран");
|
||
|
||
const post = await ctx.db.get(args.postId);
|
||
if (!post) throw new Error("Побарувањето не е пронајдено");
|
||
if (post.customerId !== session.userId) throw new Error("Само авторот може да го затвори побарувањето");
|
||
|
||
await ctx.db.patch(args.postId, { status: "closed", updatedAt: Date.now() });
|
||
return true;
|
||
},
|
||
}); |