Skip to content

Commit

Permalink
Merge pull request #455 from range-of-motion/support-creating-of-recu…
Browse files Browse the repository at this point in the history
…rrings-in-spa-prototype

Support creating of recurrings in SPA prototype
  • Loading branch information
range-of-motion authored Dec 8, 2023
2 parents 2094b11 + 98137d1 commit 981d089
Show file tree
Hide file tree
Showing 4 changed files with 118 additions and 18 deletions.
12 changes: 12 additions & 0 deletions app/Enums/RecurringInterval.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php

namespace App\Enums;

enum RecurringInterval: string
{
case daily = 'daily';
case weekly = 'weekly';
case biweekly = 'biweekly';
case monthly = 'monthly';
case yearly = 'yearly';
}
49 changes: 49 additions & 0 deletions app/Http/Controllers/Api/RecurringController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<?php

namespace App\Http\Controllers\Api;

use App\Enums\RecurringInterval;
use App\Http\Controllers\Controller;
use App\Jobs\ProcessRecurrings;
use App\Models\Recurring;
use Illuminate\Http\Request;
use Illuminate\Validation\Rule;

class RecurringController extends Controller
{
public function store(Request $request)
{
/** @var ApiKey $apiKey */
$apiKey = $request->get('apiKey');

$space = $apiKey->user->spaces()->first();

$request->validate([
'type' => ['required', 'in:earning,spending'],
'interval' => ['required', Rule::enum(RecurringInterval::class)],
'day' => ['required', 'regex:/\b(0?[1-9]|[12][0-9]|3[01])\b/'],
'start' => ['date', 'date_format:Y-m-d'],
'end' => ['nullable', 'date', 'date_format:Y-m-d'],
'tag_id' => ['nullable', 'exists:tags,id'], // TODO: CHECK IF TAG BELONGS TO USER
'description' => ['required', 'max:255'],
'amount' => ['required', 'regex:/^\d*(\.\d{1,2})?$/'],
]);

Recurring::create([
'space_id' => $space->id,
'type' => $request->input('type'),
'interval' => $request->input('interval'),
'day' => (int) ltrim($request->input('day'), 0),
'starts_on' => $request->input('start'),
'ends_on' => $request->input('end'),
'tag_id' => $request->input('tag_id'),
'description' => $request->input('description'),
'amount' => (int) ($request->input('amount') * 100),
'currency_id' => $space->currency_id,
]);

ProcessRecurrings::dispatch();

return [];
}
}
71 changes: 53 additions & 18 deletions resources/assets/js/prototype/screens/Transactions/Create.vue
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@ const tags = ref([]);
const type = ref('earning');
const tagId = ref(null);
const happened_on = ref(new Date().toJSON().slice(0, 10));
const happenedOn = ref(new Date().toJSON().slice(0, 10));
const description = ref('');
const amount = ref(10.00);
const isRecurring = ref(false);
const recurringInterval = ref('monthly');
const fetchTags = () => {
fetch('/api/tags', { headers: { 'api-key': localStorage.getItem('api_key') } })
Expand All @@ -23,23 +25,45 @@ const fetchTags = () => {
};
const create = () => {
axios.post('/api/transactions', {
type: type.value,
tag_id: tagId.value,
happened_on: happened_on.value,
description: description.value,
amount: amount.value,
}, {
headers: {
'api-key': localStorage.getItem('api_key'),
},
})
.then(() => {
router.push({ name: 'transactions.index' });
if (isRecurring.value === true) {
axios.post('/api/recurrings', {
type: type.value,
interval: recurringInterval.value,
day: happenedOn.value.split('-')[2],
start: happenedOn.value,
tag_id: tagId.value,
description: description.value,
amount: amount.value,
}, {
headers: {
'api-key': localStorage.getItem('api_key'),
},
})
.catch(() => {
alert('Something went wrong');
});
.then(() => {
router.push({ name: 'transactions.index' });
})
.catch(() => {
alert('Something went wrong');
});
} else {
axios.post('/api/transactions', {
type: type.value,
tag_id: tagId.value,
happened_on: happenedOn.value,
description: description.value,
amount: amount.value,
}, {
headers: {
'api-key': localStorage.getItem('api_key'),
},
})
.then(() => {
router.push({ name: 'transactions.index' });
})
.catch(() => {
alert('Something went wrong');
});
}
};
fetchTags();
Expand All @@ -63,7 +87,7 @@ fetchTags();
</div>
<div>
<label class="mb-2 block text-sm dark:text-white">Date</label>
<input class="w-full px-3.5 py-2.5 text-sm dark:text-white dark:bg-gray-700 border border-gray-200 dark:border-gray-600 rounded-lg" type="text" v-model="happened_on" />
<input class="w-full px-3.5 py-2.5 text-sm dark:text-white dark:bg-gray-700 border border-gray-200 dark:border-gray-600 rounded-lg" type="text" v-model="happenedOn" />
</div>
<div>
<label class="mb-2 block text-sm dark:text-white">Description</label>
Expand All @@ -73,6 +97,17 @@ fetchTags();
<label class="mb-2 block text-sm dark:text-white">Amount</label>
<input class="w-full px-3.5 py-2.5 text-sm dark:text-white dark:bg-gray-700 border border-gray-200 dark:border-gray-600 rounded-lg" type="text" v-model="amount" />
</div>
<div class="flex space-x-2">
<button class="mt-0.5 w-5 h-5 border rounded-md" :class="{ 'bg-gray-900 border-none': isRecurring }" @click="isRecurring = !isRecurring"></button>
<button class="flex-1 text-left text-sm dark:text-white" @click="isRecurring = !isRecurring">This is a recurring transaction—create it for me in the future</button>
</div>
<div v-if="isRecurring" class="flex flex-wrap gap-2">
<button class="px-3 py-2 text-sm border border-gray-200 rounded-lg" :class="{ 'bg-gray-100': recurringInterval === 'yearly' }" @click="recurringInterval = 'yearly'">Yearly</button>
<button class="px-3 py-2 text-sm border border-gray-200 rounded-lg" :class="{ 'bg-gray-100': recurringInterval === 'monthly' }" @click="recurringInterval = 'monthly'">Monthly</button>
<button class="px-3 py-2 text-sm border border-gray-200 rounded-lg" :class="{ 'bg-gray-100': recurringInterval === 'biweekly' }" @click="recurringInterval = 'biweekly'">Biweekly</button>
<button class="px-3 py-2 text-sm border border-gray-200 rounded-lg" :class="{ 'bg-gray-100': recurringInterval === 'weekly' }" @click="recurringInterval = 'weekly'">Weekly</button>
<button class="px-3 py-2 text-sm border border-gray-200 rounded-lg" :class="{ 'bg-gray-100': recurringInterval === 'daily' }" @click="recurringInterval = 'daily'">Daily</button>
</div>
<button class="w-full py-3 text-sm text-white bg-gray-900 dark:bg-gray-950 rounded-lg" @click="create()">Create</button>
</div>
</div>
Expand Down
4 changes: 4 additions & 0 deletions routes/api.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<?php

use App\Http\Controllers\Api\LogInController;
use App\Http\Controllers\Api\RecurringController;
use App\Http\Controllers\Api\SettingsController;
use App\Http\Controllers\Api\TagController;
use App\Http\Controllers\Api\TransactionController;
Expand All @@ -13,6 +14,9 @@
Route::resource('transactions', TransactionController::class)
->only(['index', 'store']);

Route::resource('recurrings', RecurringController::class)
->only(['store']);

Route::resource('tags', TagController::class)
->only(['index']);

Expand Down

0 comments on commit 981d089

Please sign in to comment.