Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Migrate client to Nuxt #258

Closed
wants to merge 109 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
109 commits
Select commit Hold shift + click to select a range
1f853e8
Work in progress
JhumanJ Dec 9, 2023
12778fa
Migrating amplitude and crisp plugin/composable
JhumanJ Dec 9, 2023
6ee56ac
Started to refactor pages
JhumanJ Dec 9, 2023
5c4dc2a
WIP
JhumanJ Dec 11, 2023
a3a9254
WIP
JhumanJ Dec 14, 2023
fa20945
WIP
JhumanJ Dec 15, 2023
be891b0
WIP
JhumanJ Dec 15, 2023
5ee599f
Fix dislpay of mb file upload size
JhumanJ Dec 15, 2023
7c2db20
Figured out auth & middlewares
JhumanJ Dec 16, 2023
3b798c1
WI
JhumanJ Dec 18, 2023
f3e4dcd
Fix validation select issue
JhumanJ Dec 18, 2023
bb51990
Refactoring stores and templates pages to comp. api
JhumanJ Dec 19, 2023
8e70d6b
Finishing the templates pages
JhumanJ Dec 19, 2023
4b00835
Merge branch 'vue-3' into migrate-to-nuxt
JhumanJ Dec 19, 2023
eda1af9
fix collapsible
JhumanJ Dec 19, 2023
aac4d1d
Finish reworking most templates pages
JhumanJ Dec 19, 2023
5640f43
Reworked workspaces store
JhumanJ Dec 19, 2023
933f95e
Working on home page and modal
JhumanJ Dec 19, 2023
df2fa4c
Fix dropdown
JhumanJ Dec 20, 2023
b598a16
Fix modal
JhumanJ Dec 20, 2023
af5656c
Fixed form creation
JhumanJ Dec 20, 2023
bab8517
Fixed most of the form/show pages
JhumanJ Dec 20, 2023
5a07064
Updated cors dependency
JhumanJ Dec 21, 2023
aa38aac
fix custom domain warning
formsdev Dec 21, 2023
3d92637
NuxtLink migration (#262)
chiragchhatrala Dec 22, 2023
57ba033
Tiny fixes + start pre-rendering
JhumanJ Dec 22, 2023
5c093b7
resolve conflicts
JhumanJ Dec 22, 2023
56e5335
migrate-to-nuxt-useappconfig (#263)
chiragchhatrala Dec 22, 2023
d93b696
Working on form/show and editor
JhumanJ Dec 24, 2023
8db2b09
Globally import form inputs to fix resolve
JhumanJ Dec 24, 2023
e2dd029
Remove vform - working on form public page
JhumanJ Dec 24, 2023
5df3245
Remove initform mixin
JhumanJ Dec 24, 2023
b4365b5
Work in progress for form create guess user
JhumanJ Dec 25, 2023
6a0aef8
Allow longer TLDs
JhumanJ Dec 29, 2023
6fd2985
Nuxt Migration notifications (#265)
formsdev Dec 31, 2023
178424a
Migrate to nuxt settings page AND remove axios (#266)
formsdev Jan 2, 2024
bd27d3a
Fix alert styling + bug fixes and cleaning
JhumanJ Jan 2, 2024
2be72e0
Refactor notifications + add shadow
JhumanJ Jan 2, 2024
3638ef8
Fix vselect issue
JhumanJ Jan 2, 2024
d1d8e62
Working on page pre-rendering
JhumanJ Jan 2, 2024
b3740dc
Created NotionPages store
JhumanJ Jan 2, 2024
db57793
Added sitemap on nuxt side
JhumanJ Jan 2, 2024
752c3d2
Sitemap done, working on aws amplify
JhumanJ Jan 2, 2024
55debc5
Adding missing module
JhumanJ Jan 2, 2024
6900fd5
Remove axios and commit backend changes to sitemap
JhumanJ Jan 2, 2024
a51ccfe
Fix notifications
JhumanJ Jan 3, 2024
3fcf173
fix guestpage editor (#269)
formsdev Jan 3, 2024
a6d8827
Remove appconfig in favor of runtimeconfig
JhumanJ Jan 3, 2024
52af13d
Merge branch 'migrate-to-nuxt' of https://github.com/JhumanJ/OpnForm …
JhumanJ Jan 3, 2024
2d41b18
Fixed amplitude bugs, and added staging environment
JhumanJ Jan 3, 2024
866d44b
Added amplify file
JhumanJ Jan 3, 2024
a0deb78
Change basdirectory amplify
JhumanJ Jan 3, 2024
ff9823b
Fix loading bar position
JhumanJ Jan 4, 2024
33458f2
Fix custom redirect (#273)
formsdev Jan 4, 2024
9594157
Dirty form handling - nuxt migration (#272)
formsdev Jan 4, 2024
f87e3f1
SEO meta nuxt migration (#274)
formsdev Jan 4, 2024
2207c8c
migrate to nuxt useClipboard (#268)
formsdev Jan 5, 2024
cc7d64a
Set middleware on pages (#278)
formsdev Jan 5, 2024
95b8853
add robots.txt (#276)
formsdev Jan 5, 2024
04170ca
404 page migration (#277)
formsdev Jan 5, 2024
9c7d46b
Templates pages migration (#275)
formsdev Jan 5, 2024
32f98d3
Fixed custom domain issues
JhumanJ Jan 5, 2024
8346fe7
Merge changes from main
JhumanJ Jan 5, 2024
8b92f24
Merge branch 'migrate-to-nuxt' of https://github.com/JhumanJ/OpnForm …
JhumanJ Jan 5, 2024
27ece34
NuxtImg Migration (#279)
formsdev Jan 5, 2024
75f6ca2
Update package json
JhumanJ Jan 5, 2024
4ada3c5
Fix build script
JhumanJ Jan 5, 2024
07219f8
Add loglevel param
JhumanJ Jan 6, 2024
2e660c1
Disable page pre-rendering
JhumanJ Jan 6, 2024
01e9ad0
Attempt to allow svgs
JhumanJ Jan 6, 2024
22f2fb6
Add console to generate tax exports
JhumanJ Jan 8, 2024
0809200
Fix form maxfile size + clean formResource
JhumanJ Jan 8, 2024
ce0c738
Fix SVGs with NuxtImage
JhumanJ Jan 8, 2024
e4177b0
Add .env file at AWS build time
JhumanJ Jan 8, 2024
fcc5e5b
tRGIGGER deploy
JhumanJ Jan 8, 2024
7329367
Fix issue
JhumanJ Jan 8, 2024
f1755fc
ANother attrempt
JhumanJ Jan 8, 2024
d83c51f
Fix typo
JhumanJ Jan 8, 2024
f26d02c
Fix env?
JhumanJ Jan 8, 2024
aba61b2
Attempt to simplify build
JhumanJ Jan 8, 2024
26ca536
Enable swr caching instead of prerenderign
JhumanJ Jan 8, 2024
77532f8
Better image compression
JhumanJ Jan 8, 2024
dd83528
JWT token IP restriction
JhumanJ Jan 9, 2024
5647fba
Merge main
JhumanJ Jan 9, 2024
c01f566
Unify domain validation regex to fix custom domain issues
JhumanJ Jan 10, 2024
32c0963
Last attempt at nuxt images efficiency
JhumanJ Jan 10, 2024
158dba6
Improve image optimization again
JhumanJ Jan 10, 2024
9750406
Remove NuxtImg for non asset files
JhumanJ Jan 10, 2024
7665539
Restore templates pages cache
JhumanJ Jan 10, 2024
668b021
Remove useless images + fix templates show page
JhumanJ Jan 10, 2024
630ae1d
image optimization caching + fix hydratation issue form template page
JhumanJ Jan 11, 2024
5a39788
URL generation (front&back) + fixed authJWT for SSR
JhumanJ Jan 11, 2024
aa0b9ae
Fix composable issue
JhumanJ Jan 11, 2024
838ddcf
Fix form share page
JhumanJ Jan 11, 2024
198aac2
Embeddable form as a nuxt middleware
JhumanJ Jan 12, 2024
5d78fc6
Merge branch 'main' into migrate-to-nuxt
JhumanJ Jan 12, 2024
4115b44
Fix URL for embeddable middleware
JhumanJ Jan 12, 2024
a0513c4
Debugging embeddable on amplify
JhumanJ Jan 12, 2024
ea7041b
Add custom domain support
JhumanJ Jan 12, 2024
b29cdf8
No follow for non-production env
JhumanJ Jan 12, 2024
be43c5c
Fix sentry nuxt and custom domain redirect
JhumanJ Jan 12, 2024
57ce0e6
remove api prefix from routes (#280)
formsdev Jan 12, 2024
ff7e1ac
nuxt migration -file upload - WIP (#271)
formsdev Jan 13, 2024
bf98497
Fix local file upload
JhumanJ Jan 13, 2024
91432c4
Fix file submissions preview
JhumanJ Jan 13, 2024
1cfd6ef
API redirect to back-end from nuxt
JhumanJ Jan 14, 2024
cc12a2d
API redirect to back-end from nuxt
JhumanJ Jan 14, 2024
675b88f
Remove old JS app, update deploy script
JhumanJ Jan 14, 2024
8096267
Fix tests, added gh action nuxt step
JhumanJ Jan 14, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
16 changes: 0 additions & 16 deletions .eslintrc.js

This file was deleted.

21 changes: 21 additions & 0 deletions .github/workflows/laravel.yml
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,27 @@ jobs:
path: storage/logs/laravel.log
retention-days: 3

build-nuxt-app:
runs-on: ubuntu-latest
name: Build the Nuxt app

steps:
- name: Checkout code
uses: actions/checkout@v2

- name: Get into client folder
run: cd client

- uses: actions/cache@v2
with:
path: ~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-

- name: Prepare the environment
run: cp .env.example .env

- name: Install npm dependencies
run: npm install --no-audit --no-progress --silent

Expand Down
12 changes: 8 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ It takes 1 minute to try out the builder for free. You'll have high availability

### Docker installation 🐳

There's a `Dockerfile` for building a self-contained docker image including databases, webservers etc.
> ⚠️ **Warning**: the Docker setup is currently not working as we're migrating the front-end to Nuxt. [Track progress here](https://github.com/JhumanJ/OpnForm/issues/283).

This can be built and run locally but is also hosted publicly on docker hub at `jhumanj/opnform` and is generally best run directly from there.

Expand Down Expand Up @@ -154,8 +154,11 @@ First, let's work with the codebase and its dependencies.
# Get the code!
git clone [email protected]:JhumanJ/OpnForm.git && cd OpnForm

# Install PHP and JS dependencies
composer install && npm install
# Install PHP dependencies
composer install

# Install JS dependencies
cd client && npm install

# Compile assets (see the scripts section in package.json)
npm run dev # or build
Expand Down Expand Up @@ -186,7 +189,8 @@ Now, create an S3 bucket (or equivalent). Create an IAM user with access to this

OpnForm is a standard web application built with:
- [Laravel](https://laravel.com/) PHP framework
- [Vue.js](https://vuejs.org/) front-end framework
- [NuxtJs](https://nuxt.com/) Front-end SSR framework
- [Vue.js 3](https://vuejs.org/) Front-end framework
- [TailwindCSS](https://tailwindcss.com/)

## Contribute
Expand Down
17 changes: 17 additions & 0 deletions amplify.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
version: 1
frontend:
phases:
preBuild:
commands:
- cd client
- npm ci
build:
commands:
- npm run build
artifacts:
baseDirectory: client/.amplify-hosting
files:
- '**/*'
cache:
paths:
- client/node_modules/**/*
252 changes: 252 additions & 0 deletions app/Console/Commands/Tax/GenerateTaxExport.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,252 @@
<?php

namespace App\Console\Commands\Tax;

use App\Exports\Tax\ArrayExport;
use Carbon\Carbon;
use Illuminate\Console\Command;
use Laravel\Cashier\Cashier;
use Stripe\Invoice;

class GenerateTaxExport extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'stripe:generate-stripe-export
{--start-date= : Start date (YYYY-MM-DD)}
{--end-date= : End date (YYYY-MM-DD)}
{--full-month : Use the full month of the start date}';

/**
* The console command description.
*
* @var string
*/
protected $description = 'Compute Stripe VAT per country';

const EU_TAX_RATES = [
"AT" => 20,
"BE" => 21,
"BG" => 20,
"HR" => 25,
"CY" => 19,
"CZ" => 21,
"DK" => 25,
"EE" => 20,
"FI" => 24,
"FR" => 20,
"DE" => 19,
"GR" => 24,
"HU" => 27,
"IE" => 23,
"IT" => 22,
"LV" => 21,
"LT" => 21,
"LU" => 17,
"MT" => 18,
"NL" => 21,
"PL" => 23,
"PT" => 23,
"RO" => 19,
"SK" => 20,
"SI" => 22,
"ES" => 21,
"SE" => 25
];

/**
* Execute the console command.
*
* @return int
*/
public function handle()
{
// iterate through all Stripe invoices
$startDate = $this->option('start-date');
$endDate = $this->option('end-date');

// Validate the date format
if ($startDate && !Carbon::createFromFormat('Y-m-d', $startDate)) {
$this->error('Invalid start date format. Use YYYY-MM-DD.');
return Command::FAILURE;
}

if ($endDate && !Carbon::createFromFormat('Y-m-d', $endDate)) {
$this->error('Invalid end date format. Use YYYY-MM-DD.');
return Command::FAILURE;
} else if (!$endDate && $this->option('full-month')) {
$endDate = Carbon::parse($startDate)->endOfMonth()->endOfDay()->format('Y-m-d');
}

$this->info('Start date: ' . $startDate);
$this->info('End date: ' . $endDate);

$processedInvoices = [];

// Create a progress bar
$queryOptions = [
'limit' => 100,
'expand' => ['data.customer', 'data.customer.address', 'data.customer.tax_ids', 'data.payment_intent',
'data.payment_intent.payment_method', 'data.charge.balance_transaction'],
'status' => 'paid',
];
if ($startDate) {
$queryOptions['created']['gte'] = Carbon::parse($startDate)->startOfDay()->timestamp;
}
if ($endDate) {
$queryOptions['created']['lte'] = Carbon::parse($endDate)->endOfDay()->timestamp;
}

$invoices = Cashier::stripe()->invoices->all($queryOptions);
$bar = $this->output->createProgressBar();
$bar->start();
$paymentNotSuccessfulCount = 0;
$totalInvoice = 0;

do {
foreach ($invoices as $invoice) {
// Ignore if payment was refunded
if (($invoice->payment_intent->status ?? null) !== 'succeeded') {
$paymentNotSuccessfulCount++;
continue;
}

$processedInvoices[] = $this->formatInvoice($invoice);
$totalInvoice++;

// Advance the progress bar
$bar->advance();
}

$queryOptions['starting_after'] = end($invoices->data)->id;

sleep(5);
$invoices = $invoices->all($queryOptions);
} while ($invoices->has_more);

$bar->finish();
$this->line('');

$aggregatedReport = $this->aggregateReport($processedInvoices);

$filePath = 'tax-export-per-invoice_' . $startDate . '_' . $endDate . '.xlsx';
$this->exportAsXlsx($processedInvoices, $filePath);

$aggregatedReportFilePath = 'tax-export-aggregated_' . $startDate . '_' . $endDate . '.xlsx';
$this->exportAsXlsx($aggregatedReport, $aggregatedReportFilePath);

// Display the results
$this->info('Total invoices: ' . $totalInvoice . ' (with ' . $paymentNotSuccessfulCount . ' payment not successful or trial free invoice)');

return Command::SUCCESS;
}

private function aggregateReport($invoices): array
{
// Sum invoices per country
$aggregatedReport = [];
foreach ($invoices as $invoice) {
$country = $invoice['cust_country'];
$customerType = is_null($invoice['cust_vat_id']) && $this->isEuropeanCountry($country) ? 'individual' : 'business';
if (!isset($aggregatedReport[$country])) {
$defaultVal = [
'count' => 0,
'total_usd' => 0,
'tax_total_usd' => 0,
'total_after_tax_usd' => 0,
'total_eur' => 0,
'tax_total_eur' => 0,
'total_after_tax_eur' => 0,
];
$aggregatedReport[$country] = [
'individual' => $defaultVal,
'business' => $defaultVal
];
}
$aggregatedReport[$country][$customerType]['count']++;
$aggregatedReport[$country][$customerType]['total_usd'] = ($aggregatedReport[$country][$customerType]['total_usd'] ?? 0) + $invoice['total_usd'];
$aggregatedReport[$country][$customerType]['tax_total_usd'] = ($aggregatedReport[$country][$customerType]['tax_total_usd'] ?? 0) + $invoice['tax_total_usd'];
$aggregatedReport[$country][$customerType]['total_after_tax_usd'] = ($aggregatedReport[$country][$customerType]['total_after_tax_usd'] ?? 0) + $invoice['total_after_tax_usd'];
$aggregatedReport[$country][$customerType]['total_eur'] = ($aggregatedReport[$country][$customerType]['total_eur'] ?? 0) + $invoice['total_eur'];
$aggregatedReport[$country][$customerType]['tax_total_eur'] = ($aggregatedReport[$country][$customerType]['tax_total_eur'] ?? 0) + $invoice['tax_total_eur'];
$aggregatedReport[$country][$customerType]['total_after_tax_eur'] = ($aggregatedReport[$country][$customerType]['total_after_tax_eur'] ?? 0) + $invoice['total_after_tax_eur'];
}

$finalReport = [];
foreach ($aggregatedReport as $country => $data) {
foreach ($data as $customerType => $aggData) {
$finalReport[] = [
'country' => $country,
'customer_type' => $customerType,
...$aggData
];
}
}
return $finalReport;
}

private function formatInvoice(Invoice $invoice): array
{
$country = $invoice->customer->address->country ?? $invoice->payment_intent->payment_method->card->country ?? null;

$vatId = $invoice->customer->tax_ids->data[0]->value ?? null;
$taxRate = $this->computeTaxRate($country, $vatId);

$taxAmountCollectedUsd = $taxRate > 0 ? $invoice->total * $taxRate / ($taxRate + 100) : 0;
$totalEur = $invoice->charge->balance_transaction->amount;
$taxAmountCollectedEur = $taxRate > 0 ? $totalEur * $taxRate / ($taxRate + 100) : 0;

return [
'invoice_id' => $invoice->id,
'created_at' => Carbon::createFromTimestamp($invoice->created)->format('Y-m-d H:i:s'),
'cust_id' => $invoice->customer->id,
'cust_vat_id' => $vatId,
'cust_country' => $country,
'tax_rate' => $taxRate,
'total_usd' => $invoice->total / 100,
'tax_total_usd' => $taxAmountCollectedUsd / 100,
'total_after_tax_usd' => ($invoice->total - $taxAmountCollectedUsd) / 100,
'total_eur' => $totalEur / 100,
'tax_total_eur' => $taxAmountCollectedEur / 100,
'total_after_tax_eur' => ($totalEur - $taxAmountCollectedEur) / 100,
];
}

private function computeTaxRate($countryCode, $vatId)
{
// Since we're a French company, for France, always apply 20% VAT
if ($countryCode == 'FR' ||
is_null($countryCode) ||
empty($countryCode)) {
return self::EU_TAX_RATES['FR'];
}

if ($taxRate = (self::EU_TAX_RATES[$countryCode] ?? null)) {
// If VAT ID is provided, then TAX is 0%
if (!$vatId) return $taxRate;
}

return 0;
}

private function isEuropeanCountry($countryCode)
{
return isset(self::EU_TAX_RATES[$countryCode]);
}

private function exportAsXlsx($data, $filename)
{
if (count($data) == 0) {
$this->info('Empty data. No file generated.');
return;
}

(new ArrayExport($data))->store($filename, 'local', \Maatwebsite\Excel\Excel::XLSX);
$this->line('File generated: ' . storage_path('app/' . $filename));
}


}
2 changes: 1 addition & 1 deletion app/Exceptions/Handler.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ protected function unauthenticated($request, AuthenticationException $exception)
{
return $request->expectsJson()
? response()->json(['message' => $exception->getMessage()], 401)
: redirect()->guest(url('/login'));
: redirect(front_url('login'));
}

public function report(Throwable $exception)
Expand Down
27 changes: 27 additions & 0 deletions app/Exports/Tax/ArrayExport.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

namespace App\Exports\Tax;

use Maatwebsite\Excel\Concerns\Exportable;
use Maatwebsite\Excel\Concerns\FromArray;
use Maatwebsite\Excel\Concerns\WithHeadings;

class ArrayExport implements FromArray, WithHeadings
{
use Exportable;

public function __construct(public array $data)
{
}

public function array(): array
{
return $this->data;
}

public function headings(): array
{
return array_keys($this->data[0]);
}
}

Loading
Loading