feat: transactions api done
Signed-off-by: Ameya Shenoy <shenoy.ameya@gmail.com>
This commit is contained in:
parent
d7295d9f31
commit
1ae3e0ce2b
9 changed files with 202 additions and 74 deletions
|
|
@ -16,17 +16,49 @@ Including another URLconf
|
|||
from django.conf import settings
|
||||
from django.conf.urls.static import static
|
||||
from django.contrib import admin
|
||||
from django.urls import path
|
||||
from drf_yasg.views import get_schema_view
|
||||
from drf_yasg import openapi
|
||||
from rest_framework import permissions
|
||||
from django.urls import include, path, re_path
|
||||
from foldbank.api import AccountsAPI, TransactionsAPI, UpcomingRecurringPaymentAPI
|
||||
|
||||
schema_view = get_schema_view(
|
||||
openapi.Info(
|
||||
title="Grapevine API",
|
||||
default_version="v1",
|
||||
description="",
|
||||
terms_of_service="https://gvine.app/terms/",
|
||||
contact=openapi.Contact(email="support@gvine.app"),
|
||||
license=openapi.License(name="BSD License"),
|
||||
),
|
||||
public=True,
|
||||
permission_classes=[permissions.AllowAny],
|
||||
)
|
||||
|
||||
urlpatterns = [
|
||||
path("admin/", admin.site.urls),
|
||||
path("api/v1/transactions/", TransactionsAPI.as_view()),
|
||||
path("api/v1/accounts/", AccountsAPI.as_view()),
|
||||
path("api/v1/recurringPayments/upcoming/", UpcomingRecurringPaymentAPI.as_view()),
|
||||
re_path(
|
||||
r"^swagger(?P<format>\.json|\.yaml)$",
|
||||
schema_view.without_ui(cache_timeout=0),
|
||||
name="schema-json",
|
||||
),
|
||||
re_path(
|
||||
r"^swagger/$",
|
||||
schema_view.with_ui("swagger", cache_timeout=0),
|
||||
name="schema-swagger-ui",
|
||||
),
|
||||
re_path(
|
||||
r"^redoc/$",
|
||||
schema_view.with_ui("redoc", cache_timeout=0),
|
||||
name="schema-redoc",
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
|
||||
if settings.DEBUG:
|
||||
urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
|
||||
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
||||
|
|
|
|||
|
|
@ -84,6 +84,12 @@ class UserAdminConfig(UserAdmin):
|
|||
)
|
||||
|
||||
|
||||
class TransactionConfig(admin.ModelAdmin):
|
||||
list_display = (
|
||||
"from_account",
|
||||
"created_at",
|
||||
)
|
||||
|
||||
|
||||
# Register your models here.
|
||||
admin.site.register(User, UserAdminConfig)
|
||||
|
|
@ -91,6 +97,6 @@ admin.site.register(Tag)
|
|||
admin.site.register(Bank)
|
||||
admin.site.register(Account)
|
||||
admin.site.register(RecurringPayment)
|
||||
admin.site.register(Transaction)
|
||||
admin.site.register(Transaction, TransactionConfig)
|
||||
admin.site.register(RecurringPaymentTransactionLink)
|
||||
|
||||
|
|
|
|||
|
|
@ -88,7 +88,7 @@ class LimitOffsetPagination(_LimitOffsetPagination):
|
|||
|
||||
|
||||
def get_transactions(user):
|
||||
return Transaction.objects.filter(from_account__user=user).order_by("created_at")
|
||||
return Transaction.objects.filter(from_account__user=user).order_by("-created_at")
|
||||
|
||||
|
||||
class TransactionsAPI(generics.ListAPIView):
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
# standard imports
|
||||
# standard importspopulatedb
|
||||
import datetime
|
||||
from zoneinfo import ZoneInfo
|
||||
|
||||
# third-party imports
|
||||
from django.utils.timezone import make_aware
|
||||
|
|
@ -226,6 +227,9 @@ class Command(BaseCommand):
|
|||
Account.objects.get_or_create(**account)
|
||||
|
||||
# recurring payment
|
||||
ist = ZoneInfo("Asia/Kolkata")
|
||||
today = datetime.datetime.now(tz=ist)
|
||||
yesterday = datetime.datetime.now(tz=ist) - datetime.timedelta(days=1)
|
||||
recurring_payments = [
|
||||
{
|
||||
"from_account": Account.objects.get(user=nishant, bank=Bank.objects.get(name="Axis Bank")),
|
||||
|
|
@ -233,9 +237,9 @@ class Command(BaseCommand):
|
|||
"amount": "36031",
|
||||
"tag": Tag.objects.get(title="Rent"),
|
||||
"defaults": {
|
||||
"created_at": make_aware(datetime.datetime(year=2023, month=1, day=23)),
|
||||
"created_at": datetime.datetime(year=2023, month=1, day=23, tzinfo=ist),
|
||||
"frequency": datetime.timedelta(days=30),
|
||||
"due_on": make_aware(datetime.datetime(year=2023, month=1, day=23)),
|
||||
"due_on": datetime.datetime(year=2023, month=1, day=23, tzinfo=ist),
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
@ -244,9 +248,9 @@ class Command(BaseCommand):
|
|||
"amount": "36031",
|
||||
"tag": Tag.objects.get(title="Rent"),
|
||||
"defaults": {
|
||||
"created_at": make_aware(datetime.datetime(year=2023, month=1, day=20)),
|
||||
"created_at": datetime.datetime(year=2023, month=1, day=20, tzinfo=ist),
|
||||
"frequency": datetime.timedelta(days=30),
|
||||
"due_on": make_aware(datetime.datetime(year=2023, month=2, day=19)),
|
||||
"due_on": datetime.datetime(year=2023, month=2, day=19, tzinfo=ist),
|
||||
}
|
||||
},
|
||||
]
|
||||
|
|
@ -261,7 +265,7 @@ class Command(BaseCommand):
|
|||
"amount": "3940",
|
||||
"tag": Tag.objects.get(title="Tag"),
|
||||
"defaults": {
|
||||
"created_at": make_aware(datetime.datetime(year=2023, month=1, day=23, hour=11, minute=17)),
|
||||
"created_at": datetime.datetime(year=today.year, month=today.month, day=today.day, hour=11, minute=17, tzinfo=ist),
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
@ -270,7 +274,7 @@ class Command(BaseCommand):
|
|||
"amount": "35",
|
||||
"tag": Tag.objects.get(title="Food & Drinks", sub_category="Drink"),
|
||||
"defaults": {
|
||||
"created_at": make_aware(datetime.datetime(year=2023, month=1, day=23, hour=23, minute=45)),
|
||||
"created_at": datetime.datetime(year=today.year, month=today.month, day=today.day, hour=23, minute=45, tzinfo=ist),
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
@ -279,7 +283,7 @@ class Command(BaseCommand):
|
|||
"amount": "2399",
|
||||
"tag": Tag.objects.get(title="Groceries"),
|
||||
"defaults": {
|
||||
"created_at": make_aware(datetime.datetime(year=2023, month=1, day=23, hour=5, minute=37)),
|
||||
"created_at": datetime.datetime(year=yesterday.year, month=yesterday.month, day=yesterday.day, hour=5, minute=37, tzinfo=ist),
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
@ -288,7 +292,7 @@ class Command(BaseCommand):
|
|||
"amount": "312",
|
||||
"tag": Tag.objects.get(title="Food & Drinks", sub_category="Food"),
|
||||
"defaults": {
|
||||
"created_at": make_aware(datetime.datetime(year=2023, month=1, day=23, hour=12, minute=17)),
|
||||
"created_at": datetime.datetime(year=yesterday.year, month=yesterday.month, day=yesterday.day, hour=12, minute=17, tzinfo=ist),
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
@ -297,7 +301,7 @@ class Command(BaseCommand):
|
|||
"amount": "75",
|
||||
"tag": Tag.objects.get(title="Transport"),
|
||||
"defaults": {
|
||||
"created_at": make_aware(datetime.datetime(year=2023, month=1, day=2, hour=10, minute=32)),
|
||||
"created_at": datetime.datetime(year=2023, month=1, day=2, hour=10, minute=32, tzinfo=ist),
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
@ -306,7 +310,7 @@ class Command(BaseCommand):
|
|||
"amount": "249",
|
||||
"tag": Tag.objects.get(title="Food & Drinks", sub_category="Swiggy"),
|
||||
"defaults": {
|
||||
"created_at": make_aware(datetime.datetime(year=2023, month=1, day=1, hour=11, minute=17)),
|
||||
"created_at": datetime.datetime(year=2023, month=1, day=1, hour=11, minute=17, tzinfo=ist),
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
@ -315,11 +319,13 @@ class Command(BaseCommand):
|
|||
"amount": "199",
|
||||
"tag": Tag.objects.get(title="Subscription", sub_category="Netflix"),
|
||||
"defaults": {
|
||||
"created_at": make_aware(datetime.datetime(year=2023, month=1, day=23, hour=23, minute=45)),
|
||||
"created_at": datetime.datetime(year=2022, month=12, day=31, hour=23, minute=45, tzinfo=ist),
|
||||
}
|
||||
},
|
||||
]
|
||||
# Transaction.objects.all().delete()
|
||||
for t in transactions:
|
||||
Transaction.objects.get_or_create(**t)
|
||||
tnx, _ = Transaction.objects.get_or_create(**t)
|
||||
tnx.created_at = t["defaults"]["created_at"]
|
||||
tnx.save()
|
||||
|
||||
|
|
|
|||
5
dev.env
5
dev.env
|
|
@ -1,8 +1,9 @@
|
|||
# Django
|
||||
SECRET_KEY=sample
|
||||
DEBUG=true
|
||||
ALLOWED_HOSTS=0.0.0.0,localhost,127.0.0.1,*
|
||||
CSRF_TRUSTED_ORIGINS=https://foldbank.codingcoffee.me
|
||||
ALLOWED_HOSTS=0.0.0.0,localhost,127.0.0.1,localhost:3000
|
||||
CSRF_TRUSTED_ORIGINS=http://localhost:3000,https://foldbank.codingcoffee.me
|
||||
CORS_ALLOWED_ORIGINS=http://localhost:3000,https://foldbank.codingcoffee.me
|
||||
DJANGO_LOG_LEVEL=INFO
|
||||
# Django - Postgres
|
||||
POSTGRES_SERVER=postgres-db
|
||||
|
|
|
|||
|
|
@ -3,15 +3,15 @@
|
|||
version: '3.7'
|
||||
|
||||
services:
|
||||
# web:
|
||||
# image: node:16.19.1-alpine3.17
|
||||
# restart: unless-stopped
|
||||
# volumes:
|
||||
# - ./web:/app
|
||||
# working_dir: /app
|
||||
# entrypoint: npm run dev
|
||||
# ports:
|
||||
# - 3000:3000
|
||||
web:
|
||||
image: node:16.19.1-alpine3.17
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- ./web:/app
|
||||
working_dir: /app
|
||||
entrypoint: npm run dev
|
||||
ports:
|
||||
- 3000:3000
|
||||
|
||||
backend:
|
||||
image: foldbank-backend-dev
|
||||
|
|
|
|||
|
|
@ -1,64 +1,109 @@
|
|||
"use client"
|
||||
|
||||
import React from 'react'
|
||||
import styles from './recentTnx.module.css'
|
||||
import { BsTriangleFill } from 'react-icons/bs';
|
||||
import SingleTransaction from './SingleTransaction'
|
||||
import useSWR from 'swr'
|
||||
|
||||
|
||||
export default function RecentTransactions() {
|
||||
const transactions = [
|
||||
{
|
||||
title: "Fenny's Banglore",
|
||||
datetime: "Today, 11:17 am",
|
||||
amount: "3,940",
|
||||
tag: "Tag",
|
||||
}, {
|
||||
title: "Sendoor",
|
||||
datetime: "Today, 11:45 pm",
|
||||
amount: "35",
|
||||
tag: "Food & Drinks",
|
||||
icon_type: "drink",
|
||||
}, {
|
||||
title: "Reliance Fresh",
|
||||
datetime: "Yesterday, 5:37 pm",
|
||||
amount: "2,399",
|
||||
tag: "Groceries",
|
||||
icon_type: "groceries",
|
||||
}, {
|
||||
title: "Chai Point",
|
||||
datetime: "Yesterday, 12:17 pm",
|
||||
amount: "312",
|
||||
tag: "Food & Drinks",
|
||||
icon_type: "food",
|
||||
}, {
|
||||
title: "Uber",
|
||||
datetime: "Jan 2, 10:32 am",
|
||||
amount: "75",
|
||||
tag: "Transport",
|
||||
icon_type: "transport",
|
||||
}, {
|
||||
title: "Swiggy",
|
||||
datetime: "Jan 1, 11:17 pm",
|
||||
amount: "249",
|
||||
tag: "Food & Drinks",
|
||||
icon_type: "swiggy",
|
||||
}, {
|
||||
title: "Netflix",
|
||||
datetime: "Dec 31, 11:59 pm",
|
||||
amount: "199",
|
||||
tag: "Subsciption",
|
||||
icon_type: "netflix",
|
||||
const fetcher = (...args) => fetch(...args).then((res) => res.json())
|
||||
const { data, error } = useSWR(`http://localhost:8000/api/v1/transactions/`, fetcher)
|
||||
|
||||
if (error) return <div>Error: Failed to load</div>
|
||||
if (!data) return <div>Loading...</div>
|
||||
|
||||
const transactions = data.results
|
||||
|
||||
// const transactions = [
|
||||
// {
|
||||
// title: "Fenny's Banglore",
|
||||
// datetime: "Today, 11:17 am",
|
||||
// amount: "3,940",
|
||||
// tag: "Tag",
|
||||
// }, {
|
||||
// title: "Sendoor",
|
||||
// datetime: "Today, 11:45 pm",
|
||||
// amount: "35",
|
||||
// tag: "Food & Drinks",
|
||||
// icon_type: "drink",
|
||||
// }, {
|
||||
// title: "Reliance Fresh",
|
||||
// datetime: "Yesterday, 5:37 pm",
|
||||
// amount: "2,399",
|
||||
// tag: "Groceries",
|
||||
// icon_type: "groceries",
|
||||
// }, {
|
||||
// title: "Chai Point",
|
||||
// datetime: "Yesterday, 12:17 pm",
|
||||
// amount: "312",
|
||||
// tag: "Food & Drinks",
|
||||
// icon_type: "food",
|
||||
// }, {
|
||||
// title: "Uber",
|
||||
// datetime: "Jan 2, 10:32 am",
|
||||
// amount: "75",
|
||||
// tag: "Transport",
|
||||
// icon_type: "transport",
|
||||
// }, {
|
||||
// title: "Swiggy",
|
||||
// datetime: "Jan 1, 11:17 pm",
|
||||
// amount: "249",
|
||||
// tag: "Food & Drinks",
|
||||
// icon_type: "swiggy",
|
||||
// }, {
|
||||
// title: "Netflix",
|
||||
// datetime: "Dec 31, 11:59 pm",
|
||||
// amount: "199",
|
||||
// tag: "Subsciption",
|
||||
// icon_type: "netflix",
|
||||
// }
|
||||
// ]
|
||||
|
||||
const relateive_date = (date) => {
|
||||
const today = new Date();
|
||||
let fuzzy;
|
||||
|
||||
if (today.getYear() == date.getYear()) {
|
||||
if (today.getMonth() == date.getMonth()) {
|
||||
if (today.getDate() == date.getDate()) {
|
||||
fuzzy = 'Today';
|
||||
} else if (today.getDate() == date.getDate() + 1) {
|
||||
fuzzy = 'Yesterday';
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
if (fuzzy == undefined){
|
||||
const monthNames = [
|
||||
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
|
||||
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
|
||||
];
|
||||
fuzzy = `${monthNames[date.getMonth()]} ${date.getDate()}`
|
||||
}
|
||||
|
||||
let hours = date.getHours()
|
||||
let ampm = 'AM';
|
||||
if (hours >= 12) {
|
||||
ampm = 'PM';
|
||||
hours -= 12;
|
||||
}
|
||||
|
||||
fuzzy = fuzzy + `, ${hours}:${date.getMinutes()} ${ampm}`
|
||||
return fuzzy
|
||||
}
|
||||
|
||||
|
||||
const recent_transactions = [];
|
||||
transactions.forEach((transaction) => {
|
||||
recent_transactions.push(
|
||||
<SingleTransaction
|
||||
title={transaction.title}
|
||||
tag={transaction.tag}
|
||||
datetime={transaction.datetime}
|
||||
title={transaction.to_account.holders_name}
|
||||
tag={transaction.tag.title}
|
||||
datetime={relateive_date(new Date(transaction.created_at))}
|
||||
amount={transaction.amount}
|
||||
icon_type={transaction.icon_type}
|
||||
icon_type={transaction.tag.icon_type}
|
||||
/>
|
||||
)
|
||||
})
|
||||
|
|
|
|||
37
web/package-lock.json
generated
37
web/package-lock.json
generated
|
|
@ -23,6 +23,7 @@
|
|||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0",
|
||||
"react-icons": "^4.7.1",
|
||||
"swr": "^2.1.0",
|
||||
"typescript": "4.9.5"
|
||||
}
|
||||
},
|
||||
|
|
@ -3901,6 +3902,20 @@
|
|||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/swr": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/swr/-/swr-2.1.0.tgz",
|
||||
"integrity": "sha512-4hYl5p3/KalQ1MORealM79g/DtLohmud6yyfXw5l4wjtFksYUnocRFudvyaoUtgj3FrVNK9lS25Av9dsZYvz0g==",
|
||||
"dependencies": {
|
||||
"use-sync-external-store": "^1.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"pnpm": "7"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.11.0 || ^17.0.0 || ^18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/synckit": {
|
||||
"version": "0.8.5",
|
||||
"resolved": "https://registry.npmjs.org/synckit/-/synckit-0.8.5.tgz",
|
||||
|
|
@ -4061,6 +4076,14 @@
|
|||
"punycode": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/use-sync-external-store": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz",
|
||||
"integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==",
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/which": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
||||
|
|
@ -6776,6 +6799,14 @@
|
|||
"resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
|
||||
"integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="
|
||||
},
|
||||
"swr": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/swr/-/swr-2.1.0.tgz",
|
||||
"integrity": "sha512-4hYl5p3/KalQ1MORealM79g/DtLohmud6yyfXw5l4wjtFksYUnocRFudvyaoUtgj3FrVNK9lS25Av9dsZYvz0g==",
|
||||
"requires": {
|
||||
"use-sync-external-store": "^1.2.0"
|
||||
}
|
||||
},
|
||||
"synckit": {
|
||||
"version": "0.8.5",
|
||||
"resolved": "https://registry.npmjs.org/synckit/-/synckit-0.8.5.tgz",
|
||||
|
|
@ -6895,6 +6926,12 @@
|
|||
"punycode": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"use-sync-external-store": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz",
|
||||
"integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==",
|
||||
"requires": {}
|
||||
},
|
||||
"which": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@
|
|||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0",
|
||||
"react-icons": "^4.7.1",
|
||||
"swr": "^2.1.0",
|
||||
"typescript": "4.9.5"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue