New: Transaction Log List

This commit is contained in:
2026-06-21 21:20:58 +02:00
parent c84a583992
commit 092db58101
7 changed files with 373 additions and 4 deletions

2
proto

Submodule proto updated: 382266dac5...880f93b0ff

View File

@@ -8,6 +8,7 @@ export default function Header() {
<Link to="/">Home</Link>
<Link to="/todo">Todo</Link>
<Link to="/shorten">Shorten</Link>
<Link to="/transaction">Transaction</Link>
<TanStackQueryHeaderUser />
</div>
);

View File

@@ -0,0 +1,66 @@
import { For, Match, Switch } from "solid-js";
import { listTransactions } from "~/gen/proto/transaction/v1/transaction-TransactionService_connectquery";
import type { Transaction } from "~/gen/proto/transaction/v1/transaction_pb";
import { useConnectQuery } from "~/integrations/connect-query/solid";
const TransactionLog = () => {
const query = useConnectQuery(listTransactions);
return (
<div class="bg-card p-4">
<h2 class="p-2 my-2">List of Transactions</h2>
<Switch fallback={<div>Loading...</div>}>
<Match when={query.isError}>
<div>Error: {query.error?.message}</div>
</Match>
<Match when={query.isLoading}>
<div>Loading...</div>
</Match>
<Match when={query.data}>
<table class="border w-full border-collapse p-2">
<thead>
<tr>
<td class="border p-2">Amount</td>
<td class="border p-2">Transaction Date</td>
<td class="border p-2">Transaction ID</td>
<td class="border p-2">Description</td>
<td class="border p-2">Created At</td>
</tr>
</thead>
<tbody>
<For each={query.data?.transactions}>
{(item) => <Transaction item={item} />}
</For>
</tbody>
</table>
</Match>
</Switch>
</div>
);
};
const Transaction = (props: { item: Transaction }) => {
return (
<tr>
<td class="border p-2">{props.item.amount}</td>
<td class="border p-2">
{props.item.transactionDate
? new Date(
Number(props.item.transactionDate.seconds) * 1000,
).toLocaleDateString()
: ""}
</td>
<td class="border p-2">{props.item.transactionId}</td>
<td class="border p-2">{props.item.description}</td>
<td class="border p-2">
{props.item.createdAt
? new Date(
Number(props.item.createdAt.seconds) * 1000,
).toLocaleDateString()
: ""}
</td>
</tr>
);
};
export default TransactionLog;

View File

@@ -0,0 +1,25 @@
// @generated by protoc-gen-connect-query v2.2.0 with parameter "target=ts"
// @generated from file proto/transaction/v1/transaction.proto (package proto.transaction.v1, syntax proto3)
/* eslint-disable */
import { TransactionService } from "./transaction_pb";
/**
* @generated from rpc proto.transaction.v1.TransactionService.CreateTransaction
*/
export const createTransaction = TransactionService.method.createTransaction;
/**
* @generated from rpc proto.transaction.v1.TransactionService.ListTransactions
*/
export const listTransactions = TransactionService.method.listTransactions;
/**
* @generated from rpc proto.transaction.v1.TransactionService.GetTransaction
*/
export const getTransaction = TransactionService.method.getTransaction;
/**
* @generated from rpc proto.transaction.v1.TransactionService.DeleteTransaction
*/
export const deleteTransaction = TransactionService.method.deleteTransaction;

View File

@@ -0,0 +1,245 @@
// @generated by protoc-gen-es v2.12.0 with parameter "target=ts"
// @generated from file proto/transaction/v1/transaction.proto (package proto.transaction.v1, syntax proto3)
/* eslint-disable */
import type { GenFile, GenMessage, GenService } from "@bufbuild/protobuf/codegenv2";
import { fileDesc, messageDesc, serviceDesc } from "@bufbuild/protobuf/codegenv2";
import { file_buf_validate_validate } from "../../../buf/validate/validate_pb";
import type { Timestamp } from "@bufbuild/protobuf/wkt";
import { file_google_protobuf_timestamp } from "@bufbuild/protobuf/wkt";
import type { Message } from "@bufbuild/protobuf";
/**
* Describes the file proto/transaction/v1/transaction.proto.
*/
export const file_proto_transaction_v1_transaction: GenFile = /*@__PURE__*/
fileDesc("CiZwcm90by90cmFuc2FjdGlvbi92MS90cmFuc2FjdGlvbi5wcm90bxIUcHJvdG8udHJhbnNhY3Rpb24udjEi8AEKC1RyYW5zYWN0aW9uEhYKDnRyYW5zYWN0aW9uX2lkGAEgASgFEhwKC2Rlc2NyaXB0aW9uGAIgASgJQge6SARyAhABEioKBmFtb3VudBgDIAEoCUIaukgXchUyE14tP1xkKyhcLlxkezEsMn0pPyQSGQoIY2F0ZWdvcnkYBCABKAlCB7pIBHICEAESNAoQdHJhbnNhY3Rpb25fZGF0ZRgFIAEoCzIaLmdvb2dsZS5wcm90b2J1Zi5UaW1lc3RhbXASLgoKY3JlYXRlZF9hdBgGIAEoCzIaLmdvb2dsZS5wcm90b2J1Zi5UaW1lc3RhbXAitQEKGENyZWF0ZVRyYW5zYWN0aW9uUmVxdWVzdBIcCgtkZXNjcmlwdGlvbhgBIAEoCUIHukgEcgIQARIqCgZhbW91bnQYAiABKAlCGrpIF3IVMhNeLT9cZCsoXC5cZHsxLDJ9KT8kEhkKCGNhdGVnb3J5GAMgASgJQge6SARyAhABEjQKEHRyYW5zYWN0aW9uX2RhdGUYBCABKAsyGi5nb29nbGUucHJvdG9idWYuVGltZXN0YW1wIlMKGUNyZWF0ZVRyYW5zYWN0aW9uUmVzcG9uc2USNgoLdHJhbnNhY3Rpb24YASABKAsyIS5wcm90by50cmFuc2FjdGlvbi52MS5UcmFuc2FjdGlvbiIZChdMaXN0VHJhbnNhY3Rpb25zUmVxdWVzdCJTChhMaXN0VHJhbnNhY3Rpb25zUmVzcG9uc2USNwoMdHJhbnNhY3Rpb25zGAEgAygLMiEucHJvdG8udHJhbnNhY3Rpb24udjEuVHJhbnNhY3Rpb24iOAoVR2V0VHJhbnNhY3Rpb25SZXF1ZXN0Eh8KDnRyYW5zYWN0aW9uX2lkGAEgASgFQge6SAQaAiAAIlAKFkdldFRyYW5zYWN0aW9uUmVzcG9uc2USNgoLdHJhbnNhY3Rpb24YASABKAsyIS5wcm90by50cmFuc2FjdGlvbi52MS5UcmFuc2FjdGlvbiI7ChhEZWxldGVUcmFuc2FjdGlvblJlcXVlc3QSHwoOdHJhbnNhY3Rpb25faWQYASABKAVCB7pIBBoCIAAiJwoZRGVsZXRlVHJhbnNhY3Rpb25SZXNwb25zZRIKCgJvaxgBIAEoCDLoAwoSVHJhbnNhY3Rpb25TZXJ2aWNlEnYKEUNyZWF0ZVRyYW5zYWN0aW9uEi4ucHJvdG8udHJhbnNhY3Rpb24udjEuQ3JlYXRlVHJhbnNhY3Rpb25SZXF1ZXN0Gi8ucHJvdG8udHJhbnNhY3Rpb24udjEuQ3JlYXRlVHJhbnNhY3Rpb25SZXNwb25zZSIAEnMKEExpc3RUcmFuc2FjdGlvbnMSLS5wcm90by50cmFuc2FjdGlvbi52MS5MaXN0VHJhbnNhY3Rpb25zUmVxdWVzdBouLnByb3RvLnRyYW5zYWN0aW9uLnYxLkxpc3RUcmFuc2FjdGlvbnNSZXNwb25zZSIAEm0KDkdldFRyYW5zYWN0aW9uEisucHJvdG8udHJhbnNhY3Rpb24udjEuR2V0VHJhbnNhY3Rpb25SZXF1ZXN0GiwucHJvdG8udHJhbnNhY3Rpb24udjEuR2V0VHJhbnNhY3Rpb25SZXNwb25zZSIAEnYKEURlbGV0ZVRyYW5zYWN0aW9uEi4ucHJvdG8udHJhbnNhY3Rpb24udjEuRGVsZXRlVHJhbnNhY3Rpb25SZXF1ZXN0Gi8ucHJvdG8udHJhbnNhY3Rpb24udjEuRGVsZXRlVHJhbnNhY3Rpb25SZXNwb25zZSIAQugBChhjb20ucHJvdG8udHJhbnNhY3Rpb24udjFCEFRyYW5zYWN0aW9uUHJvdG9QAVpIZ2l0LmtvY29kZXIueHl6L3Z0L3Nob3J0ZW5lci9pbnRlcm5hbC9wcm90by90cmFuc2FjdGlvbi92MTt0cmFuc2FjdGlvbnYxogIDUFRYqgIUUHJvdG8uVHJhbnNhY3Rpb24uVjHKAhRQcm90b1xUcmFuc2FjdGlvblxWMeICIFByb3RvXFRyYW5zYWN0aW9uXFYxXEdQQk1ldGFkYXRh6gIWUHJvdG86OlRyYW5zYWN0aW9uOjpWMWIGcHJvdG8z", [file_buf_validate_validate, file_google_protobuf_timestamp]);
/**
* @generated from message proto.transaction.v1.Transaction
*/
export type Transaction = Message<"proto.transaction.v1.Transaction"> & {
/**
* @generated from field: int32 transaction_id = 1;
*/
transactionId: number;
/**
* @generated from field: string description = 2;
*/
description: string;
/**
* @generated from field: string amount = 3;
*/
amount: string;
/**
* @generated from field: string category = 4;
*/
category: string;
/**
* @generated from field: google.protobuf.Timestamp transaction_date = 5;
*/
transactionDate?: Timestamp | undefined;
/**
* @generated from field: google.protobuf.Timestamp created_at = 6;
*/
createdAt?: Timestamp | undefined;
};
/**
* Describes the message proto.transaction.v1.Transaction.
* Use `create(TransactionSchema)` to create a new message.
*/
export const TransactionSchema: GenMessage<Transaction> = /*@__PURE__*/
messageDesc(file_proto_transaction_v1_transaction, 0);
/**
* @generated from message proto.transaction.v1.CreateTransactionRequest
*/
export type CreateTransactionRequest = Message<"proto.transaction.v1.CreateTransactionRequest"> & {
/**
* @generated from field: string description = 1;
*/
description: string;
/**
* @generated from field: string amount = 2;
*/
amount: string;
/**
* @generated from field: string category = 3;
*/
category: string;
/**
* @generated from field: google.protobuf.Timestamp transaction_date = 4;
*/
transactionDate?: Timestamp | undefined;
};
/**
* Describes the message proto.transaction.v1.CreateTransactionRequest.
* Use `create(CreateTransactionRequestSchema)` to create a new message.
*/
export const CreateTransactionRequestSchema: GenMessage<CreateTransactionRequest> = /*@__PURE__*/
messageDesc(file_proto_transaction_v1_transaction, 1);
/**
* @generated from message proto.transaction.v1.CreateTransactionResponse
*/
export type CreateTransactionResponse = Message<"proto.transaction.v1.CreateTransactionResponse"> & {
/**
* @generated from field: proto.transaction.v1.Transaction transaction = 1;
*/
transaction?: Transaction | undefined;
};
/**
* Describes the message proto.transaction.v1.CreateTransactionResponse.
* Use `create(CreateTransactionResponseSchema)` to create a new message.
*/
export const CreateTransactionResponseSchema: GenMessage<CreateTransactionResponse> = /*@__PURE__*/
messageDesc(file_proto_transaction_v1_transaction, 2);
/**
* @generated from message proto.transaction.v1.ListTransactionsRequest
*/
export type ListTransactionsRequest = Message<"proto.transaction.v1.ListTransactionsRequest"> & {
};
/**
* Describes the message proto.transaction.v1.ListTransactionsRequest.
* Use `create(ListTransactionsRequestSchema)` to create a new message.
*/
export const ListTransactionsRequestSchema: GenMessage<ListTransactionsRequest> = /*@__PURE__*/
messageDesc(file_proto_transaction_v1_transaction, 3);
/**
* @generated from message proto.transaction.v1.ListTransactionsResponse
*/
export type ListTransactionsResponse = Message<"proto.transaction.v1.ListTransactionsResponse"> & {
/**
* @generated from field: repeated proto.transaction.v1.Transaction transactions = 1;
*/
transactions: Transaction[];
};
/**
* Describes the message proto.transaction.v1.ListTransactionsResponse.
* Use `create(ListTransactionsResponseSchema)` to create a new message.
*/
export const ListTransactionsResponseSchema: GenMessage<ListTransactionsResponse> = /*@__PURE__*/
messageDesc(file_proto_transaction_v1_transaction, 4);
/**
* @generated from message proto.transaction.v1.GetTransactionRequest
*/
export type GetTransactionRequest = Message<"proto.transaction.v1.GetTransactionRequest"> & {
/**
* @generated from field: int32 transaction_id = 1;
*/
transactionId: number;
};
/**
* Describes the message proto.transaction.v1.GetTransactionRequest.
* Use `create(GetTransactionRequestSchema)` to create a new message.
*/
export const GetTransactionRequestSchema: GenMessage<GetTransactionRequest> = /*@__PURE__*/
messageDesc(file_proto_transaction_v1_transaction, 5);
/**
* @generated from message proto.transaction.v1.GetTransactionResponse
*/
export type GetTransactionResponse = Message<"proto.transaction.v1.GetTransactionResponse"> & {
/**
* @generated from field: proto.transaction.v1.Transaction transaction = 1;
*/
transaction?: Transaction | undefined;
};
/**
* Describes the message proto.transaction.v1.GetTransactionResponse.
* Use `create(GetTransactionResponseSchema)` to create a new message.
*/
export const GetTransactionResponseSchema: GenMessage<GetTransactionResponse> = /*@__PURE__*/
messageDesc(file_proto_transaction_v1_transaction, 6);
/**
* @generated from message proto.transaction.v1.DeleteTransactionRequest
*/
export type DeleteTransactionRequest = Message<"proto.transaction.v1.DeleteTransactionRequest"> & {
/**
* @generated from field: int32 transaction_id = 1;
*/
transactionId: number;
};
/**
* Describes the message proto.transaction.v1.DeleteTransactionRequest.
* Use `create(DeleteTransactionRequestSchema)` to create a new message.
*/
export const DeleteTransactionRequestSchema: GenMessage<DeleteTransactionRequest> = /*@__PURE__*/
messageDesc(file_proto_transaction_v1_transaction, 7);
/**
* @generated from message proto.transaction.v1.DeleteTransactionResponse
*/
export type DeleteTransactionResponse = Message<"proto.transaction.v1.DeleteTransactionResponse"> & {
/**
* @generated from field: bool ok = 1;
*/
ok: boolean;
};
/**
* Describes the message proto.transaction.v1.DeleteTransactionResponse.
* Use `create(DeleteTransactionResponseSchema)` to create a new message.
*/
export const DeleteTransactionResponseSchema: GenMessage<DeleteTransactionResponse> = /*@__PURE__*/
messageDesc(file_proto_transaction_v1_transaction, 8);
/**
* @generated from service proto.transaction.v1.TransactionService
*/
export const TransactionService: GenService<{
/**
* @generated from rpc proto.transaction.v1.TransactionService.CreateTransaction
*/
createTransaction: {
methodKind: "unary";
input: typeof CreateTransactionRequestSchema;
output: typeof CreateTransactionResponseSchema;
},
/**
* @generated from rpc proto.transaction.v1.TransactionService.ListTransactions
*/
listTransactions: {
methodKind: "unary";
input: typeof ListTransactionsRequestSchema;
output: typeof ListTransactionsResponseSchema;
},
/**
* @generated from rpc proto.transaction.v1.TransactionService.GetTransaction
*/
getTransaction: {
methodKind: "unary";
input: typeof GetTransactionRequestSchema;
output: typeof GetTransactionResponseSchema;
},
/**
* @generated from rpc proto.transaction.v1.TransactionService.DeleteTransaction
*/
deleteTransaction: {
methodKind: "unary";
input: typeof DeleteTransactionRequestSchema;
output: typeof DeleteTransactionResponseSchema;
},
}> = /*@__PURE__*/
serviceDesc(file_proto_transaction_v1_transaction, 0);

View File

@@ -12,6 +12,7 @@ import { Route as rootRouteImport } from './routes/__root'
import { Route as TodoRouteImport } from './routes/todo'
import { Route as ShortenRouteImport } from './routes/shorten'
import { Route as IndexRouteImport } from './routes/index'
import { Route as TransactionIndexRouteImport } from './routes/transaction/index'
const TodoRoute = TodoRouteImport.update({
id: '/todo',
@@ -28,35 +29,44 @@ const IndexRoute = IndexRouteImport.update({
path: '/',
getParentRoute: () => rootRouteImport,
} as any)
const TransactionIndexRoute = TransactionIndexRouteImport.update({
id: '/transaction/',
path: '/transaction/',
getParentRoute: () => rootRouteImport,
} as any)
export interface FileRoutesByFullPath {
'/': typeof IndexRoute
'/shorten': typeof ShortenRoute
'/todo': typeof TodoRoute
'/transaction/': typeof TransactionIndexRoute
}
export interface FileRoutesByTo {
'/': typeof IndexRoute
'/shorten': typeof ShortenRoute
'/todo': typeof TodoRoute
'/transaction': typeof TransactionIndexRoute
}
export interface FileRoutesById {
__root__: typeof rootRouteImport
'/': typeof IndexRoute
'/shorten': typeof ShortenRoute
'/todo': typeof TodoRoute
'/transaction/': typeof TransactionIndexRoute
}
export interface FileRouteTypes {
fileRoutesByFullPath: FileRoutesByFullPath
fullPaths: '/' | '/shorten' | '/todo'
fullPaths: '/' | '/shorten' | '/todo' | '/transaction/'
fileRoutesByTo: FileRoutesByTo
to: '/' | '/shorten' | '/todo'
id: '__root__' | '/' | '/shorten' | '/todo'
to: '/' | '/shorten' | '/todo' | '/transaction'
id: '__root__' | '/' | '/shorten' | '/todo' | '/transaction/'
fileRoutesById: FileRoutesById
}
export interface RootRouteChildren {
IndexRoute: typeof IndexRoute
ShortenRoute: typeof ShortenRoute
TodoRoute: typeof TodoRoute
TransactionIndexRoute: typeof TransactionIndexRoute
}
declare module '@tanstack/solid-router' {
@@ -82,6 +92,13 @@ declare module '@tanstack/solid-router' {
preLoaderRoute: typeof IndexRouteImport
parentRoute: typeof rootRouteImport
}
'/transaction/': {
id: '/transaction/'
path: '/transaction'
fullPath: '/transaction/'
preLoaderRoute: typeof TransactionIndexRouteImport
parentRoute: typeof rootRouteImport
}
}
}
@@ -89,6 +106,7 @@ const rootRouteChildren: RootRouteChildren = {
IndexRoute: IndexRoute,
ShortenRoute: ShortenRoute,
TodoRoute: TodoRoute,
TransactionIndexRoute: TransactionIndexRoute,
}
export const routeTree = rootRouteImport
._addFileChildren(rootRouteChildren)

View File

@@ -0,0 +1,14 @@
import { createFileRoute } from "@tanstack/solid-router";
import TransactionLog from "~/components/transactions/Log";
export const Route = createFileRoute("/transaction/")({ component: App });
function App() {
return (
<div class="p-4 flex flex-col gap-y-4">
<h1 class="text-xl text-bold">Transaction Log</h1>
{/* <CreateRedirectionForm /> */}
<TransactionLog />
</div>
);
}