feat: «Mi día», the few tickets I pick for today
A home panel with the tickets picked for today (10 at most), crossed out
as they get done and counted («1 de 3 terminados»), my open tickets to
add with one click, and what was left unfinished on my last day to carry
over. «☀ Mi día» on a ticket adds it or takes it out. The day is the
person's own (YYYY-MM-DD from the browser); private, no history (Mongo
`my_days`).
API: myDay(day), addToMyDay, removeFromMyDay; GET /api/me/day,
PUT/DELETE /api/me/day/{issue}. MCP: my_day.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,46 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { InjectModel } from '@nestjs/mongoose';
|
||||
import {
|
||||
MyDayRecord,
|
||||
MyDaysRepository,
|
||||
} from '@task-stream/api/tracker/application';
|
||||
import { Model } from 'mongoose';
|
||||
import { ReadModels } from './read-model.schemas';
|
||||
|
||||
type Stored = MyDayRecord & { _id?: unknown };
|
||||
|
||||
const strip = ({ _id: _ignored, ...record }: Stored): MyDayRecord => ({
|
||||
...record,
|
||||
// Mongo leaves an emptied list out.
|
||||
issueIds: record.issueIds ?? [],
|
||||
});
|
||||
|
||||
@Injectable()
|
||||
export class MongoMyDaysRepository extends MyDaysRepository {
|
||||
constructor(
|
||||
@InjectModel(ReadModels.MyDay) private readonly days: Model<Stored>,
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
async find(userId: string, day: string): Promise<MyDayRecord | null> {
|
||||
const doc = await this.days.findOne({ userId, day }).lean<Stored>();
|
||||
return doc ? strip(doc) : null;
|
||||
}
|
||||
|
||||
async latestBefore(userId: string, day: string): Promise<MyDayRecord | null> {
|
||||
const doc = await this.days
|
||||
.findOne({ userId, day: { $lt: day } })
|
||||
.sort({ day: -1 })
|
||||
.lean<Stored>();
|
||||
return doc ? strip(doc) : null;
|
||||
}
|
||||
|
||||
async save(record: MyDayRecord): Promise<void> {
|
||||
await this.days.updateOne(
|
||||
{ userId: record.userId, day: record.day },
|
||||
{ $set: record },
|
||||
{ upsert: true },
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -111,6 +111,7 @@ export const ReadModels = {
|
||||
AlertTrace: 'AlertTrace',
|
||||
TreeFollow: 'TreeFollow',
|
||||
IssueReminder: 'IssueReminder',
|
||||
MyDay: 'MyDay',
|
||||
Sprint: 'SprintReadModel',
|
||||
GitIntegration: 'GitIntegration',
|
||||
OutgoingWebhook: 'OutgoingWebhook',
|
||||
@@ -385,6 +386,15 @@ export const IssueReminderSchema = SchemaFactory.createForClass(
|
||||
IssueReminderSchema.index({ sentAt: 1, at: 1 });
|
||||
IssueReminderSchema.index({ userId: 1, issueId: 1 });
|
||||
|
||||
@Schema({ ...LOOSE, collection: 'my_days' })
|
||||
class MyDayDocument {
|
||||
@Prop({ type: String }) userId!: string;
|
||||
@Prop({ type: String }) day!: string;
|
||||
@Prop({ type: [String] }) issueIds!: string[];
|
||||
}
|
||||
export const MyDaySchema = SchemaFactory.createForClass(MyDayDocument);
|
||||
MyDaySchema.index({ userId: 1, day: -1 }, { unique: true });
|
||||
|
||||
@Schema({ ...LOOSE, collection: 'git_integrations' })
|
||||
class GitIntegrationDocument {
|
||||
/** The project's id. */
|
||||
@@ -492,6 +502,7 @@ export const READ_MODEL_SCHEMAS = [
|
||||
{ name: ReadModels.AlertTrace, schema: AlertTraceSchema },
|
||||
{ name: ReadModels.TreeFollow, schema: TreeFollowSchema },
|
||||
{ name: ReadModels.IssueReminder, schema: IssueReminderSchema },
|
||||
{ name: ReadModels.MyDay, schema: MyDaySchema },
|
||||
{ name: ReadModels.CalendarFeed, schema: CalendarFeedSchema },
|
||||
{ name: ReadModels.DashboardShare, schema: DashboardShareSchema },
|
||||
{ name: ReadModels.PushSubscription, schema: PushSubscriptionSchema },
|
||||
|
||||
@@ -17,6 +17,7 @@ import {
|
||||
AlertIntegrationsRepository,
|
||||
TreeFollowsRepository,
|
||||
IssueRemindersRepository,
|
||||
MyDaysRepository,
|
||||
GitIntegrationsRepository,
|
||||
OutgoingWebhooksRepository,
|
||||
AutomationsRepository,
|
||||
@@ -86,6 +87,7 @@ import { MongoPinsRepository } from './mongo/pins.repository';
|
||||
import { MongoAlertIntegrationsRepository } from './mongo/alert-integrations.repository';
|
||||
import { MongoTreeFollowsRepository } from './mongo/tree-follows.repository';
|
||||
import { MongoIssueRemindersRepository } from './mongo/issue-reminders.repository';
|
||||
import { MongoMyDaysRepository } from './mongo/my-days.repository';
|
||||
import { MongoGitIntegrationsRepository } from './mongo/git-integrations.repository';
|
||||
import { MongoOutgoingWebhooksRepository } from './mongo/outgoing-webhooks.repository';
|
||||
import { HttpWebhookSender } from './webhooks/http-webhook.sender';
|
||||
@@ -172,6 +174,7 @@ const PORTS = [
|
||||
provide: IssueRemindersRepository,
|
||||
useClass: MongoIssueRemindersRepository,
|
||||
},
|
||||
{ provide: MyDaysRepository, useClass: MongoMyDaysRepository },
|
||||
{
|
||||
provide: GitIntegrationsRepository,
|
||||
useClass: MongoGitIntegrationsRepository,
|
||||
|
||||
Reference in New Issue
Block a user