Skip to content
This repository has been archived by the owner on Feb 8, 2025. It is now read-only.

Commit

Permalink
Merge pull request #138 from Kerosene-Labs/BIL-6-update-fixed
Browse files Browse the repository at this point in the history
Add modal, update Tailwind config
  • Loading branch information
hlafaille authored Jan 1, 2025
2 parents 0857320 + 11f50a0 commit 5c205bc
Show file tree
Hide file tree
Showing 29 changed files with 418 additions and 162 deletions.
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package com.kerosenelabs.billtracker

import com.kerosenelabs.billtracker.service.ExpenseService
import io.swagger.v3.oas.annotations.OpenAPIDefinition
import io.swagger.v3.oas.annotations.servers.Server
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication
import org.springframework.scheduling.annotation.EnableScheduling
import org.springframework.web.bind.annotation.CrossOrigin

@OpenAPIDefinition(
Expand All @@ -15,6 +17,7 @@ import org.springframework.web.bind.annotation.CrossOrigin
)
@CrossOrigin
@SpringBootApplication
@EnableScheduling
open class BillTrackerApplication

fun main(args: Array<String>) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,10 @@ import org.springframework.http.HttpStatus
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RequestParam
import org.springframework.web.bind.annotation.ResponseStatus
import org.springframework.web.bind.annotation.RestController
import java.util.*

@RestController
@Tag(name = "Expenses", description = "Personal expenses")
Expand Down Expand Up @@ -55,12 +57,20 @@ class ExpensesController(private val expenseService: ExpenseService) {

@GetMapping("/expenses/recurringCreators")
@ResponseStatus(HttpStatus.OK)
fun getRecurringExpenseCreators(@Parameter(hidden = true) user: UserEntity): GetRecurringExpenseEventCreatorsResponse {
fun getRecurringExpenseCreators(
@Parameter(hidden = true) user: UserEntity,
@RequestParam(name = "ids", required = false) ids: List<UUID>?
): GetRecurringExpenseEventCreatorsResponse {
return GetRecurringExpenseEventCreatorsResponse(
expenseService.getRecurringExpenseEventCreatorsByUser(user)
expenseService.getRecurringExpenseEventCreatorsByUser(user, Optional.ofNullable(ids))
.stream()
.map { entity -> expenseService.mapRecurringExpenseEventCreatorEntityToRecurringExpenseEventCreator(entity) }
.map { entity ->
expenseService.mapRecurringExpenseEventCreatorEntityToRecurringExpenseEventCreator(
entity
)
}
.toList()
)
}

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.kerosenelabs.billtracker.entity

import com.kerosenelabs.billtracker.model.expense.RecurringExpenseEventCreator
import jakarta.persistence.*
import java.math.BigDecimal
import java.util.UUID
Expand All @@ -10,10 +11,18 @@ class RecurringExpenseEventCreatorEntity(
@Id @GeneratedValue(strategy = GenerationType.AUTO) var id: UUID? = null,
@Column(nullable = false) var recursEveryCalendarDay: Int = 0,
@Column(nullable = false) var amount: BigDecimal = BigDecimal.ZERO,

@ManyToOne
@JoinColumn(nullable = false)
var user: UserEntity = UserEntity(),

@ManyToOne @JoinColumn(nullable = false) var user: UserEntity = UserEntity(),
@OneToOne var supersededBy: RecurringExpenseEventCreatorEntity? = null,
@Column(nullable = false) var description: String = "",
)
) {
constructor(
recursEveryCalendarDay: Int, amount: BigDecimal, user: UserEntity, description: String
) : this(
id = null,
recursEveryCalendarDay = recursEveryCalendarDay,
amount = amount,
user = user,
supersededBy = null,
description = description
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,13 @@ package com.kerosenelabs.billtracker.repository
import com.kerosenelabs.billtracker.entity.RecurringExpenseEventCreatorEntity
import com.kerosenelabs.billtracker.entity.UserEntity
import org.springframework.data.jpa.repository.JpaRepository
import org.springframework.data.jpa.repository.Query
import org.springframework.data.repository.query.Param
import java.util.*

interface RecurringExpenseEventCreatorRepository : JpaRepository<RecurringExpenseEventCreatorEntity, UUID>{
fun findAllByUser(user: UserEntity): List<RecurringExpenseEventCreatorEntity>
}
@Query("SELECT e FROM RecurringExpenseEventCreatorEntity e WHERE e.user = :user AND (:ids IS NULL OR e.id IN :ids)")
fun findAllByUserAndOptionalIds(
@Param("user") user: UserEntity,
@Param("ids") ids: List<UUID>?
): List<RecurringExpenseEventCreatorEntity>}
Original file line number Diff line number Diff line change
Expand Up @@ -8,32 +8,34 @@ import com.kerosenelabs.billtracker.model.expense.ExpenseEventType
import com.kerosenelabs.billtracker.model.expense.RecurringExpenseEventCreator
import com.kerosenelabs.billtracker.repository.ExpenseEventRepository
import com.kerosenelabs.billtracker.repository.RecurringExpenseEventCreatorRepository
import org.hibernate.sql.ast.tree.expression.Every
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import org.springframework.scheduling.annotation.Scheduled
import org.springframework.stereotype.Service
import java.math.BigDecimal
import java.time.Instant
import java.time.LocalDate
import java.time.ZoneId
import java.util.*
import kotlin.jvm.optionals.getOrNull


@Service
class ExpenseService(
val expenseEventRepository: ExpenseEventRepository,
val recurringExpenseEventCreatorRepository: RecurringExpenseEventCreatorRepository
private val expenseEventRepository: ExpenseEventRepository,
private val recurringExpenseEventCreatorRepository: RecurringExpenseEventCreatorRepository,
) {
private val logger: Logger = LoggerFactory.getLogger(ExpenseService::class.java)

/**
* Creates a one-off expense. For example, one might create a one-off expense for purchasing dinner or groceries.
* @see ExpenseEventEntity
*/
fun createOneOffExpense(
amount: BigDecimal,
user: UserEntity,
date: Instant,
description: String
amount: BigDecimal, user: UserEntity, date: Instant, description: String
): ExpenseEventEntity {
val expenseEvent = ExpenseEventEntity(
amount = amount,
user = user,
date = date,
description = description
amount = amount, user = user, date = date, description = description
)
return expenseEventRepository.save(expenseEvent)
}
Expand All @@ -42,17 +44,11 @@ class ExpenseService(
* Creates a Recurring Expense Event Creator. These entities are picked up from scheduled tasks and create expense events on set calendar dates.
*/
fun createRecurringExpenseEventCreator(
amount: BigDecimal,
user: UserEntity,
recursEveryCalendarDay: Int,
description: String
amount: BigDecimal, user: UserEntity, recursEveryCalendarDay: Int, description: String
): RecurringExpenseEventCreatorEntity {
return recurringExpenseEventCreatorRepository.save(
RecurringExpenseEventCreatorEntity(
amount = amount,
user = user,
recursEveryCalendarDay = recursEveryCalendarDay,
description = description
amount = amount, user = user, recursEveryCalendarDay = recursEveryCalendarDay, description = description
)
)
}
Expand All @@ -66,11 +62,12 @@ class ExpenseService(
}

/**
* Helper function to get all recurring expense event creators by a user.
* Helper function to get all recurring expense event creators by a user. Optionally, you can pass in IDs to
* further filter by Recurring Expense Event Creator ID.
* @see RecurringExpenseEventCreatorEntity
*/
fun getRecurringExpenseEventCreatorsByUser(user: UserEntity): List<RecurringExpenseEventCreatorEntity> {
return recurringExpenseEventCreatorRepository.findAllByUser(user)
fun getRecurringExpenseEventCreatorsByUser(user: UserEntity, ids: Optional<List<UUID>> = Optional.empty()): List<RecurringExpenseEventCreatorEntity> {
return recurringExpenseEventCreatorRepository.findAllByUserAndOptionalIds(user, ids.getOrNull())
}

/**
Expand Down Expand Up @@ -99,4 +96,26 @@ class ExpenseService(
recursEveryCalendarDay = entity.recursEveryCalendarDay,
)
}

/**
* Scheduled task that iterates over all recurring expense creators, searching for those that occur today and need posting.
*/
@Scheduled(cron = "0 0 0 * * ?")
fun postRecurringExpenses() {
logger.info("Starting post of recurring expenses")
val creators = recurringExpenseEventCreatorRepository.findAll();
val now = LocalDate.now();
val zoneId = ZoneId.systemDefault()
for (creator in creators) {
if (creator.recursEveryCalendarDay == now.dayOfMonth) {
logger.info("Posting Expense Event: ${creator.amount} - ${creator.description}");
expenseEventRepository.save(ExpenseEventEntity(
amount = creator.amount,
description = creator.description,
recurringExpenseEventCreator = creator,
date = now.atStartOfDay(zoneId).toInstant()
))
}
}
}
}
3 changes: 1 addition & 2 deletions src/main/svelte/.prettierrc
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,5 @@
"plugins": ["prettier-plugin-svelte", "prettier-plugin-tailwindcss"],
"svelteSortOrder": "scripts-markup-options-styles",
"svelteStrictMode": false,
"svelteBracketNewLine": true,
"svelteAllowShorthand": true
"svelteBracketNewLine": false
}
36 changes: 30 additions & 6 deletions src/main/svelte/src/app.css
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,38 @@
@import "tailwindcss/components";
@import "tailwindcss/utilities";

h1 {
@apply text-2xl font-black text-neutral-100 xl:text-4xl;
:root {
/*zinc-100*/
--bg-page: #f4f4f5;
/*neutral-300*/
--bg-card: #d4d4d4;

/*neutral-900*/
--text-primary: #171717;
/*neutral-800*/
--text-muted: #262626;
}

h2 {
@apply text-xl font-black text-neutral-100 xl:text-3xl;
[data-theme="dark"] {
/*zinc-900*/
--bg-page: #18181b;
/*neutral-800*/
--bg-card: #262626;

/*neutral-100*/
--text-primary: #f5f5f5;
/*neutral-300*/
--text-muted: #d4d4d4;
}

p.subtitle {
@apply font-semibold text-neutral-400;
h1, h2, h3, h4, h5, h6 {
@apply text-primary font-black;
}

h1 {
@apply text-2xl desktop:text-3xl;
}

p {
@apply text-primary font-semibold;
}
60 changes: 36 additions & 24 deletions src/main/svelte/src/app.html
Original file line number Diff line number Diff line change
@@ -1,27 +1,39 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
<link rel="manifest" href="/manifest.json" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<script>
if ("serviceWorker" in navigator) {
navigator.serviceWorker
.register("/service-worker.js")
.then(() => console.log("Service Worker registered."))
.catch((error) =>
console.error("Service Worker registration failed:", error),
);
}
</script>
%sveltekit.head%
</head>
<body
data-sveltekit-preload-data="hover"
style="height: 100vh"
class="bg-neutral-800"
>
<div style="display: contents">%sveltekit.body%</div>
</body>
<head>
<meta charset="utf-8" />
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
<link rel="manifest" href="/manifest.json" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<script>
if ("serviceWorker" in navigator) {
navigator.serviceWorker
.register("/service-worker.js")
.then(() => console.log("Service Worker registered."))
.catch((error) =>
console.error("Service Worker registration failed:", error)
);
}

const prefersDarkScheme = window.matchMedia("(prefers-color-scheme: dark)");
if (prefersDarkScheme.matches) {
document.documentElement.setAttribute("data-theme", "dark");
} else {
document.documentElement.setAttribute("data-theme", "light");
}
prefersDarkScheme.addEventListener('change', (event) => {
const newColorScheme = event.matches ? 'dark' : 'light';
document.documentElement.setAttribute('data-theme', newColorScheme);
});

</script>
%sveltekit.head%
</head>
<body
data-sveltekit-preload-data="hover"
style="height: 100vh"
class="bg-page"
>
<div style="display: contents">%sveltekit.body%</div>
</body>
</html>
4 changes: 0 additions & 4 deletions src/main/svelte/src/lib/components/NavUserCard.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,6 @@
<div class="flex flex-col gap-4 rounded-b-xl bg-neutral-800 px-4 py-8">
{#if !loading}
<h1>Hello, {firstName}.</h1>
<p class="font-semibold text-neutral-300">
Did you know that Octopuses have three hearts, and two of them stop
beating when they swim!
</p>
{:else}
<div class="flex w-full justify-center">
<Spinner></Spinner>
Expand Down
36 changes: 0 additions & 36 deletions src/main/svelte/src/lib/components/Table.svelte

This file was deleted.

Loading

0 comments on commit 5c205bc

Please sign in to comment.