mirror of
https://github.com/crater-invoice/crater.git
synced 2025-10-27 11:41:09 -04:00
Compare commits
7 Commits
dependabot
...
fix-logout
| Author | SHA1 | Date | |
|---|---|---|---|
| bdf8fa8b5a | |||
| ba298cdb0a | |||
| 05d5ce26fd | |||
| 393fe20010 | |||
| 57bdbd2897 | |||
| 7447cc24f9 | |||
| 889d22d92c |
161
.github/workflows/uffizzi-build.yml
vendored
Normal file
161
.github/workflows/uffizzi-build.yml
vendored
Normal file
@ -0,0 +1,161 @@
|
||||
name: Build PR Image
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened,synchronize,reopened,closed]
|
||||
|
||||
jobs:
|
||||
|
||||
build-application:
|
||||
name: Build and Push `application`
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.event_name != 'pull_request' || github.event.action != 'closed' }}
|
||||
outputs:
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
steps:
|
||||
- name: Checkout git repo
|
||||
uses: actions/checkout@v3
|
||||
- name: Generate UUID image name
|
||||
id: uuid
|
||||
run: echo "UUID_TAG_APP=$(uuidgen)" >> $GITHUB_ENV
|
||||
- name: Docker metadata
|
||||
id: meta
|
||||
uses: docker/metadata-action@v3
|
||||
with:
|
||||
images: registry.uffizzi.com/${{ env.UUID_TAG_APP }}
|
||||
tags: type=raw,value=60d
|
||||
- name: Build and Push Image to registry.uffizzi.com ephemeral registry
|
||||
uses: docker/build-push-action@v2
|
||||
with:
|
||||
push: true
|
||||
context: ./
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
file: ./uffizzi/Dockerfile
|
||||
|
||||
build-nginx:
|
||||
needs:
|
||||
- build-application
|
||||
name: Build and Push `nginx`
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.event_name != 'pull_request' || github.event.action != 'closed' }}
|
||||
outputs:
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
steps:
|
||||
- name: Checkout git repo
|
||||
uses: actions/checkout@v3
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
- name: Generate UUID image name
|
||||
id: uuid
|
||||
run: echo "UUID_TAG_NGINX=$(uuidgen)" >> $GITHUB_ENV
|
||||
- name: Docker metadata
|
||||
id: meta
|
||||
uses: docker/metadata-action@v3
|
||||
with:
|
||||
images: registry.uffizzi.com/${{ env.UUID_TAG_NGINX }}
|
||||
tags: type=raw,value=60d
|
||||
- name: Build and Push Image to Uffizzi ephemeral registry
|
||||
uses: docker/build-push-action@v2
|
||||
with:
|
||||
push: true
|
||||
context: ./
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
file: ./uffizzi/nginx/Dockerfile
|
||||
build-args: |
|
||||
BASE_IMAGE=${{ needs.build-application.outputs.tags }}
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
|
||||
|
||||
build-crond:
|
||||
name: Build and Push `crond`
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.event_name != 'pull_request' || github.event.action != 'closed' }}
|
||||
outputs:
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
steps:
|
||||
- name: Checkout git repo
|
||||
uses: actions/checkout@v3
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
- name: Generate UUID image name
|
||||
id: uuid
|
||||
run: echo "UUID_TAG_CROND=$(uuidgen)" >> $GITHUB_ENV
|
||||
- name: Docker metadata
|
||||
id: meta
|
||||
uses: docker/metadata-action@v3
|
||||
with:
|
||||
images: registry.uffizzi.com/${{ env.UUID_TAG_CROND }}
|
||||
tags: type=raw,value=60d
|
||||
- name: Build and Push Image to registry.uffizzi.com ephemeral registry
|
||||
uses: docker/build-push-action@v2
|
||||
with:
|
||||
push: true
|
||||
context: ./
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
file: ./uffizzi/crond/Dockerfile
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
|
||||
|
||||
|
||||
render-compose-file:
|
||||
name: Render Docker Compose File
|
||||
# Pass output of this workflow to another triggered by `workflow_run` event.
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
compose-file-cache-key: ${{ steps.hash.outputs.hash }}
|
||||
needs:
|
||||
- build-application
|
||||
- build-nginx
|
||||
- build-crond
|
||||
steps:
|
||||
- name: Checkout git repo
|
||||
uses: actions/checkout@v3
|
||||
- name: Render Compose File
|
||||
run: |
|
||||
APP_IMAGE=$(echo ${{ needs.build-application.outputs.tags }})
|
||||
export APP_IMAGE
|
||||
NGINX_IMAGE=$(echo ${{ needs.build-nginx.outputs.tags }})
|
||||
export NGINX_IMAGE
|
||||
CROND_IMAGE=$(echo ${{ needs.build-crond.outputs.tags }})
|
||||
export CROND_IMAGE
|
||||
# Render simple template from environment variables.
|
||||
envsubst < ./uffizzi/docker-compose.uffizzi.yml > docker-compose.rendered.yml
|
||||
cat docker-compose.rendered.yml
|
||||
- name: Upload Rendered Compose File as Artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: preview-spec
|
||||
path: docker-compose.rendered.yml
|
||||
retention-days: 2
|
||||
- name: Serialize PR Event to File
|
||||
run: |
|
||||
cat << EOF > event.json
|
||||
${{ toJSON(github.event) }}
|
||||
|
||||
EOF
|
||||
- name: Upload PR Event as Artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: preview-spec
|
||||
path: event.json
|
||||
retention-days: 2
|
||||
|
||||
delete-preview:
|
||||
name: Call for Preview Deletion
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.event.action == 'closed' }}
|
||||
steps:
|
||||
# If this PR is closing, we will not render a compose file nor pass it to the next workflow.
|
||||
- name: Serialize PR Event to File
|
||||
run: echo '${{ toJSON(github.event) }}' > event.json
|
||||
- name: Upload PR Event as Artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: preview-spec
|
||||
path: event.json
|
||||
retention-days: 2
|
||||
|
||||
84
.github/workflows/uffizzi-preview.yml
vendored
Normal file
84
.github/workflows/uffizzi-preview.yml
vendored
Normal file
@ -0,0 +1,84 @@
|
||||
name: Deploy Uffizzi Preview
|
||||
|
||||
on:
|
||||
workflow_run:
|
||||
workflows:
|
||||
- "Build PR Image"
|
||||
types:
|
||||
- completed
|
||||
|
||||
|
||||
jobs:
|
||||
cache-compose-file:
|
||||
name: Cache Compose File
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
compose-file-cache-key: ${{ env.COMPOSE_FILE_HASH }}
|
||||
pr-number: ${{ env.PR_NUMBER }}
|
||||
steps:
|
||||
- name: 'Download artifacts'
|
||||
# Fetch output (zip archive) from the workflow run that triggered this workflow.
|
||||
uses: actions/github-script@v6
|
||||
with:
|
||||
script: |
|
||||
let allArtifacts = await github.rest.actions.listWorkflowRunArtifacts({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
run_id: context.payload.workflow_run.id,
|
||||
});
|
||||
let matchArtifact = allArtifacts.data.artifacts.filter((artifact) => {
|
||||
return artifact.name == "preview-spec"
|
||||
})[0];
|
||||
let download = await github.rest.actions.downloadArtifact({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
artifact_id: matchArtifact.id,
|
||||
archive_format: 'zip',
|
||||
});
|
||||
let fs = require('fs');
|
||||
fs.writeFileSync(`${process.env.GITHUB_WORKSPACE}/preview-spec.zip`, Buffer.from(download.data));
|
||||
- name: 'Unzip artifact'
|
||||
run: unzip preview-spec.zip
|
||||
- name: Read Event into ENV
|
||||
run: |
|
||||
echo 'EVENT_JSON<<EOF' >> $GITHUB_ENV
|
||||
cat event.json >> $GITHUB_ENV
|
||||
echo 'EOF' >> $GITHUB_ENV
|
||||
- name: Hash Rendered Compose File
|
||||
id: hash
|
||||
# If the previous workflow was triggered by a PR close event, we will not have a compose file artifact.
|
||||
if: ${{ fromJSON(env.EVENT_JSON).action != 'closed' }}
|
||||
run: echo "COMPOSE_FILE_HASH=$(md5sum docker-compose.rendered.yml | awk '{ print $1 }')" >> $GITHUB_ENV
|
||||
- name: Cache Rendered Compose File
|
||||
if: ${{ fromJSON(env.EVENT_JSON).action != 'closed' }}
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: docker-compose.rendered.yml
|
||||
key: ${{ env.COMPOSE_FILE_HASH }}
|
||||
|
||||
- name: Read PR Number From Event Object
|
||||
id: pr
|
||||
run: echo "PR_NUMBER=${{ fromJSON(env.EVENT_JSON).number }}" >> $GITHUB_ENV
|
||||
|
||||
- name: DEBUG - Print Job Outputs
|
||||
if: ${{ runner.debug }}
|
||||
run: |
|
||||
echo "PR number: ${{ env.PR_NUMBER }}"
|
||||
echo "Compose file hash: ${{ env.COMPOSE_FILE_HASH }}"
|
||||
cat event.json
|
||||
deploy-uffizzi-preview:
|
||||
name: Use Remote Workflow to Preview on Uffizzi
|
||||
needs:
|
||||
- cache-compose-file
|
||||
uses: UffizziCloud/preview-action/.github/workflows/reusable.yaml@v2.6.1
|
||||
with:
|
||||
# If this workflow was triggered by a PR close event, cache-key will be an empty string
|
||||
# and this reusable workflow will delete the preview deployment.
|
||||
compose-file-cache-key: ${{ needs.cache-compose-file.outputs.compose-file-cache-key }}
|
||||
compose-file-cache-path: docker-compose.rendered.yml
|
||||
server: https://app.uffizzi.com/
|
||||
pr-number: ${{ needs.cache-compose-file.outputs.pr-number }}
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
id-token: write
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@ -16,4 +16,5 @@ Homestead.yaml
|
||||
.gitkeep
|
||||
/public/docs
|
||||
/.scribe
|
||||
!storage/fonts/.gitkeep
|
||||
!storage/fonts/.gitkeep
|
||||
.DS_Store
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
FROM php:7.4-fpm
|
||||
FROM php:8.1-fpm
|
||||
|
||||
# Arguments defined in docker-compose.yml
|
||||
ARG user
|
||||
|
||||
@ -55,7 +55,7 @@ class CreateTemplateCommand extends Command
|
||||
copy(public_path("/build/img/PDF/{$type}1.png"), public_path("/build/img/PDF/{$templateName}.png"));
|
||||
copy(resource_path("/static/img/PDF/{$type}1.png"), resource_path("/static/img/PDF/{$templateName}.png"));
|
||||
|
||||
$path = resource_path("app/pdf/{$type}/{$templateName}.blade.php");
|
||||
$path = resource_path("views/app/pdf/{$type}/{$templateName}.blade.php");
|
||||
$type = ucfirst($type);
|
||||
$this->info("{$type} Template created successfully at ".$path);
|
||||
|
||||
|
||||
@ -27,6 +27,7 @@ return [
|
||||
'tokenizer',
|
||||
'JSON',
|
||||
'cURL',
|
||||
'zip',
|
||||
],
|
||||
'apache' => [
|
||||
'mod_rewrite',
|
||||
|
||||
@ -35,16 +35,7 @@
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="
|
||||
grid
|
||||
col-span-12
|
||||
mt-6
|
||||
text-center
|
||||
xl:mt-0
|
||||
sm:grid-cols-4
|
||||
xl:text-right xl:col-span-3 xl:grid-cols-1
|
||||
xxl:col-span-2
|
||||
"
|
||||
class="grid col-span-12 mt-6 text-center xl:mt-0 sm:grid-cols-4 xl:text-right xl:col-span-3 xl:grid-cols-1 xxl:col-span-2"
|
||||
>
|
||||
<div class="px-6 py-2">
|
||||
<span class="text-xs leading-5 lg:text-sm">
|
||||
@ -177,10 +168,12 @@ const getChartInvoices = computed(() => {
|
||||
return []
|
||||
})
|
||||
|
||||
const customerId = computed(() => route.params.id)
|
||||
|
||||
watch(
|
||||
route,
|
||||
() => {
|
||||
if (route.params.id) {
|
||||
() => customerId.value,
|
||||
(id) => {
|
||||
if (id && route.name === 'customers.view') {
|
||||
loadCustomer()
|
||||
}
|
||||
selectedYear.value = 'This year'
|
||||
|
||||
@ -37,32 +37,10 @@
|
||||
|
||||
<!-- Sidebar -->
|
||||
<div
|
||||
class="
|
||||
fixed
|
||||
top-0
|
||||
left-0
|
||||
hidden
|
||||
h-full
|
||||
pt-16
|
||||
pb-[6.4rem]
|
||||
ml-56
|
||||
bg-white
|
||||
xl:ml-64
|
||||
w-88
|
||||
xl:block
|
||||
"
|
||||
class="fixed top-0 left-0 hidden h-full pt-16 pb-[6.4rem] ml-56 bg-white xl:ml-64 w-88 xl:block"
|
||||
>
|
||||
<div
|
||||
class="
|
||||
flex
|
||||
items-center
|
||||
justify-between
|
||||
px-4
|
||||
pt-8
|
||||
pb-2
|
||||
border border-gray-200 border-solid
|
||||
height-full
|
||||
"
|
||||
class="flex items-center justify-between px-4 pt-8 pb-2 border border-gray-200 border-solid height-full"
|
||||
>
|
||||
<div class="mb-6">
|
||||
<BaseInput
|
||||
@ -92,14 +70,7 @@
|
||||
</template>
|
||||
|
||||
<div
|
||||
class="
|
||||
px-4
|
||||
py-1
|
||||
pb-2
|
||||
mb-1 mb-2
|
||||
text-sm
|
||||
border-b border-gray-200 border-solid
|
||||
"
|
||||
class="px-4 py-1 pb-2 mb-1 mb-2 text-sm border-b border-gray-200 border-solid"
|
||||
>
|
||||
{{ $t('general.sort_by') }}
|
||||
</div>
|
||||
@ -156,12 +127,7 @@
|
||||
|
||||
<div
|
||||
ref="estimateListSection"
|
||||
class="
|
||||
h-full
|
||||
overflow-y-scroll
|
||||
border-l border-gray-200 border-solid
|
||||
base-scroll
|
||||
"
|
||||
class="h-full overflow-y-scroll border-l border-gray-200 border-solid base-scroll"
|
||||
>
|
||||
<div v-for="(estimate, index) in estimateList" :key="index">
|
||||
<router-link
|
||||
@ -181,29 +147,11 @@
|
||||
<BaseText
|
||||
:text="estimate.customer.name"
|
||||
:length="30"
|
||||
class="
|
||||
pr-2
|
||||
mb-2
|
||||
text-sm
|
||||
not-italic
|
||||
font-normal
|
||||
leading-5
|
||||
text-black
|
||||
capitalize
|
||||
truncate
|
||||
"
|
||||
class="pr-2 mb-2 text-sm not-italic font-normal leading-5 text-black capitalize truncate"
|
||||
/>
|
||||
|
||||
<div
|
||||
class="
|
||||
mt-1
|
||||
mb-2
|
||||
text-xs
|
||||
not-italic
|
||||
font-medium
|
||||
leading-5
|
||||
text-gray-600
|
||||
"
|
||||
class="mt-1 mb-2 text-xs not-italic font-medium leading-5 text-gray-600"
|
||||
>
|
||||
{{ estimate.estimate_number }}
|
||||
</div>
|
||||
@ -220,26 +168,11 @@
|
||||
<BaseFormatMoney
|
||||
:amount="estimate.total"
|
||||
:currency="estimate.customer.currency"
|
||||
class="
|
||||
block
|
||||
mb-2
|
||||
text-xl
|
||||
not-italic
|
||||
font-semibold
|
||||
leading-8
|
||||
text-right text-gray-900
|
||||
"
|
||||
class="block mb-2 text-xl not-italic font-semibold leading-8 text-right text-gray-900"
|
||||
/>
|
||||
|
||||
<div
|
||||
class="
|
||||
text-sm
|
||||
not-italic
|
||||
font-normal
|
||||
leading-5
|
||||
text-right text-gray-600
|
||||
est-date
|
||||
"
|
||||
class="text-sm not-italic font-normal leading-5 text-right text-gray-600 est-date"
|
||||
>
|
||||
{{ estimate.formatted_estimate_date }}
|
||||
</div>
|
||||
@ -264,13 +197,7 @@
|
||||
>
|
||||
<iframe
|
||||
:src="`${shareableLink}`"
|
||||
class="
|
||||
flex-1
|
||||
border border-gray-400 border-solid
|
||||
rounded-md
|
||||
bg-white
|
||||
frame-style
|
||||
"
|
||||
class="flex-1 border border-gray-400 border-solid rounded-md bg-white frame-style"
|
||||
/>
|
||||
</div>
|
||||
</BasePage>
|
||||
@ -345,11 +272,14 @@ const getCurrentEstimateId = computed(() => {
|
||||
return null
|
||||
})
|
||||
|
||||
watch(route, (to, from) => {
|
||||
if (to.name === 'estimates.view') {
|
||||
loadEstimate()
|
||||
const estimateId = computed(() => route.params.id)
|
||||
|
||||
watch(
|
||||
() => estimateId.value,
|
||||
(id) => {
|
||||
if (id && route.name === 'estimates.view') loadEstimate()
|
||||
}
|
||||
})
|
||||
)
|
||||
|
||||
loadEstimates()
|
||||
loadEstimate()
|
||||
|
||||
@ -65,11 +65,14 @@ const getCurrentInvoiceId = computed(() => {
|
||||
return null
|
||||
})
|
||||
|
||||
watch(route, (to, from) => {
|
||||
if (to.name === 'invoices.view') {
|
||||
loadInvoice()
|
||||
const invoiceId = computed(() => route.params.id)
|
||||
|
||||
watch(
|
||||
() => invoiceId.value,
|
||||
(id) => {
|
||||
if (id && route.name === 'invoices.view') loadInvoice()
|
||||
}
|
||||
})
|
||||
)
|
||||
|
||||
async function onMarkAsSent() {
|
||||
dialogStore
|
||||
@ -286,32 +289,10 @@ onSearched = debounce(onSearched, 500)
|
||||
|
||||
<!-- sidebar -->
|
||||
<div
|
||||
class="
|
||||
fixed
|
||||
top-0
|
||||
left-0
|
||||
hidden
|
||||
h-full
|
||||
pt-16
|
||||
pb-[6.4rem]
|
||||
ml-56
|
||||
bg-white
|
||||
xl:ml-64
|
||||
w-88
|
||||
xl:block
|
||||
"
|
||||
class="fixed top-0 left-0 hidden h-full pt-16 pb-[6.4rem] ml-56 bg-white xl:ml-64 w-88 xl:block"
|
||||
>
|
||||
<div
|
||||
class="
|
||||
flex
|
||||
items-center
|
||||
justify-between
|
||||
px-4
|
||||
pt-8
|
||||
pb-2
|
||||
border border-gray-200 border-solid
|
||||
height-full
|
||||
"
|
||||
class="flex items-center justify-between px-4 pt-8 pb-2 border border-gray-200 border-solid height-full"
|
||||
>
|
||||
<div class="mb-6">
|
||||
<BaseInput
|
||||
@ -335,14 +316,7 @@ onSearched = debounce(onSearched, 500)
|
||||
</BaseButton>
|
||||
</template>
|
||||
<div
|
||||
class="
|
||||
px-2
|
||||
py-1
|
||||
pb-2
|
||||
mb-1 mb-2
|
||||
text-sm
|
||||
border-b border-gray-200 border-solid
|
||||
"
|
||||
class="px-2 py-1 pb-2 mb-1 mb-2 text-sm border-b border-gray-200 border-solid"
|
||||
>
|
||||
{{ $t('general.sort_by') }}
|
||||
</div>
|
||||
@ -399,12 +373,7 @@ onSearched = debounce(onSearched, 500)
|
||||
|
||||
<div
|
||||
ref="invoiceListSection"
|
||||
class="
|
||||
h-full
|
||||
overflow-y-scroll
|
||||
border-l border-gray-200 border-solid
|
||||
base-scroll
|
||||
"
|
||||
class="h-full overflow-y-scroll border-l border-gray-200 border-solid base-scroll"
|
||||
>
|
||||
<div v-for="(invoice, index) in invoiceList" :key="index">
|
||||
<router-link
|
||||
@ -424,29 +393,11 @@ onSearched = debounce(onSearched, 500)
|
||||
<BaseText
|
||||
:text="invoice.customer.name"
|
||||
:length="30"
|
||||
class="
|
||||
pr-2
|
||||
mb-2
|
||||
text-sm
|
||||
not-italic
|
||||
font-normal
|
||||
leading-5
|
||||
text-black
|
||||
capitalize
|
||||
truncate
|
||||
"
|
||||
class="pr-2 mb-2 text-sm not-italic font-normal leading-5 text-black capitalize truncate"
|
||||
/>
|
||||
|
||||
<div
|
||||
class="
|
||||
mt-1
|
||||
mb-2
|
||||
text-xs
|
||||
not-italic
|
||||
font-medium
|
||||
leading-5
|
||||
text-gray-600
|
||||
"
|
||||
class="mt-1 mb-2 text-xs not-italic font-medium leading-5 text-gray-600"
|
||||
>
|
||||
{{ invoice.invoice_number }}
|
||||
</div>
|
||||
@ -460,27 +411,12 @@ onSearched = debounce(onSearched, 500)
|
||||
|
||||
<div class="flex-1 whitespace-nowrap right">
|
||||
<BaseFormatMoney
|
||||
class="
|
||||
mb-2
|
||||
text-xl
|
||||
not-italic
|
||||
font-semibold
|
||||
leading-8
|
||||
text-right text-gray-900
|
||||
block
|
||||
"
|
||||
class="mb-2 text-xl not-italic font-semibold leading-8 text-right text-gray-900 block"
|
||||
:amount="invoice.total"
|
||||
:currency="invoice.customer.currency"
|
||||
/>
|
||||
<div
|
||||
class="
|
||||
text-sm
|
||||
not-italic
|
||||
font-normal
|
||||
leading-5
|
||||
text-right text-gray-600
|
||||
est-date
|
||||
"
|
||||
class="text-sm not-italic font-normal leading-5 text-right text-gray-600 est-date"
|
||||
>
|
||||
{{ invoice.formatted_invoice_date }}
|
||||
</div>
|
||||
@ -505,13 +441,7 @@ onSearched = debounce(onSearched, 500)
|
||||
>
|
||||
<iframe
|
||||
:src="`${shareableLink}`"
|
||||
class="
|
||||
flex-1
|
||||
border border-gray-400 border-solid
|
||||
bg-white
|
||||
rounded-md
|
||||
frame-style
|
||||
"
|
||||
class="flex-1 border border-gray-400 border-solid bg-white rounded-md frame-style"
|
||||
/>
|
||||
</div>
|
||||
</BasePage>
|
||||
|
||||
@ -22,31 +22,10 @@
|
||||
|
||||
<!-- Sidebar -->
|
||||
<div
|
||||
class="
|
||||
fixed
|
||||
top-0
|
||||
left-0
|
||||
hidden
|
||||
h-full
|
||||
pt-16
|
||||
pb-[6rem]
|
||||
ml-56
|
||||
bg-white
|
||||
xl:ml-64
|
||||
w-88
|
||||
xl:block
|
||||
"
|
||||
class="fixed top-0 left-0 hidden h-full pt-16 pb-[6rem] ml-56 bg-white xl:ml-64 w-88 xl:block"
|
||||
>
|
||||
<div
|
||||
class="
|
||||
flex
|
||||
items-center
|
||||
justify-between
|
||||
px-4
|
||||
pt-8
|
||||
pb-6
|
||||
border border-gray-200 border-solid
|
||||
"
|
||||
class="flex items-center justify-between px-4 pt-8 pb-6 border border-gray-200 border-solid"
|
||||
>
|
||||
<BaseInput
|
||||
v-model="searchData.searchText"
|
||||
@ -70,14 +49,7 @@
|
||||
</template>
|
||||
|
||||
<div
|
||||
class="
|
||||
px-4
|
||||
py-1
|
||||
pb-2
|
||||
mb-2
|
||||
text-sm
|
||||
border-b border-gray-200 border-solid
|
||||
"
|
||||
class="px-4 py-1 pb-2 mb-2 text-sm border-b border-gray-200 border-solid"
|
||||
>
|
||||
{{ $t('general.sort_by') }}
|
||||
</div>
|
||||
@ -159,43 +131,17 @@
|
||||
<BaseText
|
||||
:text="payment?.customer?.name"
|
||||
:length="30"
|
||||
class="
|
||||
pr-2
|
||||
mb-2
|
||||
text-sm
|
||||
not-italic
|
||||
font-normal
|
||||
leading-5
|
||||
text-black
|
||||
capitalize
|
||||
truncate
|
||||
"
|
||||
class="pr-2 mb-2 text-sm not-italic font-normal leading-5 text-black capitalize truncate"
|
||||
/>
|
||||
|
||||
<div
|
||||
class="
|
||||
mb-1
|
||||
text-xs
|
||||
not-italic
|
||||
font-medium
|
||||
leading-5
|
||||
text-gray-500
|
||||
capitalize
|
||||
"
|
||||
class="mb-1 text-xs not-italic font-medium leading-5 text-gray-500 capitalize"
|
||||
>
|
||||
{{ payment?.payment_number }}
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="
|
||||
mb-1
|
||||
text-xs
|
||||
not-italic
|
||||
font-medium
|
||||
leading-5
|
||||
text-gray-500
|
||||
capitalize
|
||||
"
|
||||
class="mb-1 text-xs not-italic font-medium leading-5 text-gray-500 capitalize"
|
||||
>
|
||||
{{ payment?.invoice_number }}
|
||||
</div>
|
||||
@ -203,15 +149,7 @@
|
||||
|
||||
<div class="flex-1 whitespace-nowrap right">
|
||||
<BaseFormatMoney
|
||||
class="
|
||||
block
|
||||
mb-2
|
||||
text-xl
|
||||
not-italic
|
||||
font-semibold
|
||||
leading-8
|
||||
text-right text-gray-900
|
||||
"
|
||||
class="block mb-2 text-xl not-italic font-semibold leading-8 text-right text-gray-900"
|
||||
:amount="payment?.amount"
|
||||
:currency="payment.customer?.currency"
|
||||
/>
|
||||
@ -313,9 +251,14 @@ const paymentDate = computed(() => {
|
||||
)
|
||||
})
|
||||
|
||||
watch(route, () => {
|
||||
loadPayment()
|
||||
})
|
||||
const paymentId = computed(() => route.params.id)
|
||||
|
||||
watch(
|
||||
() => paymentId.value,
|
||||
(id) => {
|
||||
if (id && route.name === 'payments.view') loadPayment()
|
||||
}
|
||||
)
|
||||
|
||||
loadPayments()
|
||||
loadPayment()
|
||||
|
||||
@ -78,10 +78,12 @@ let isLoading = computed(() => {
|
||||
return recurringInvoiceStore.isFetchingViewData
|
||||
})
|
||||
|
||||
const invoiceId = computed(() => route.params.id)
|
||||
|
||||
watch(
|
||||
route,
|
||||
() => {
|
||||
if (route.params.id && route.name === 'recurring-invoices.view') {
|
||||
() => invoiceId.value,
|
||||
(id) => {
|
||||
if (id && route.name === 'recurring-invoices.view') {
|
||||
loadRecurringInvoice()
|
||||
}
|
||||
},
|
||||
|
||||
40
uffizzi/.env.example
Normal file
40
uffizzi/.env.example
Normal file
@ -0,0 +1,40 @@
|
||||
APP_ENV=production
|
||||
APP_KEY=base64:kgk/4DW1vEVy7aEvet5FPp5un6PIGe/so8H0mvoUtW0=
|
||||
APP_DEBUG=true
|
||||
APP_LOG_LEVEL=debug
|
||||
APP_URL=http://crater.test
|
||||
|
||||
DB_CONNECTION=mysql
|
||||
DB_HOST=127.0.0.1
|
||||
DB_PORT=3306
|
||||
DB_DATABASE=crater
|
||||
DB_USERNAME=crater
|
||||
DB_PASSWORD=crater
|
||||
|
||||
BROADCAST_DRIVER=log
|
||||
CACHE_DRIVER=file
|
||||
QUEUE_DRIVER=sync
|
||||
SESSION_DRIVER=cookie
|
||||
SESSION_LIFETIME=1440
|
||||
|
||||
REDIS_HOST=127.0.0.1
|
||||
REDIS_PASSWORD=null
|
||||
REDIS_PORT=6379
|
||||
|
||||
MAIL_DRIVER=smtp
|
||||
MAIL_HOST=
|
||||
MAIL_PORT=
|
||||
MAIL_USERNAME=
|
||||
MAIL_PASSWORD=
|
||||
MAIL_ENCRYPTION=
|
||||
|
||||
PUSHER_APP_ID=
|
||||
PUSHER_KEY=
|
||||
PUSHER_SECRET=
|
||||
|
||||
SANCTUM_STATEFUL_DOMAINS=crater.test
|
||||
SESSION_DOMAIN=crater.test
|
||||
|
||||
TRUSTED_PROXIES="*"
|
||||
|
||||
CRON_JOB_AUTH_TOKEN=""
|
||||
64
uffizzi/Dockerfile
Normal file
64
uffizzi/Dockerfile
Normal file
@ -0,0 +1,64 @@
|
||||
FROM php:8.1-fpm
|
||||
|
||||
# Install system dependencies
|
||||
RUN apt-get update && apt-get install -y \
|
||||
git \
|
||||
curl \
|
||||
libpng-dev \
|
||||
libonig-dev \
|
||||
libxml2-dev \
|
||||
zip \
|
||||
unzip \
|
||||
libzip-dev \
|
||||
libmagickwand-dev \
|
||||
mariadb-client \
|
||||
npm
|
||||
|
||||
# Clear cache
|
||||
RUN apt-get clean && rm -rf /var/lib/apt/lists/*
|
||||
|
||||
RUN pecl install imagick \
|
||||
&& docker-php-ext-enable imagick
|
||||
|
||||
# Install PHP extensions
|
||||
RUN docker-php-ext-install pdo_mysql mbstring zip exif pcntl bcmath gd
|
||||
|
||||
# Get latest Composer
|
||||
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
|
||||
|
||||
# Create system user to run Composer and Artisan Commands
|
||||
RUN useradd -G www-data,root -u 1000 -d /home/crater-user crater-user
|
||||
RUN mkdir -p /home/crater-user/.composer && \
|
||||
chown -R crater-user:crater-user /home/crater-user
|
||||
|
||||
# Mounted volumes
|
||||
COPY ./ /var/www
|
||||
COPY ./docker-compose/php/uploads.ini /usr/local/etc/php/conf.d/uploads.ini
|
||||
COPY ./uffizzi/.env.example /var/www/.env
|
||||
|
||||
# Set working directory
|
||||
WORKDIR /var/www
|
||||
|
||||
RUN chown -R crater-user:crater-user ./
|
||||
RUN chmod -R 775 composer.json composer.lock \
|
||||
composer.lock storage/framework/ \
|
||||
storage/logs/ bootstrap/cache/ /home/crater-user/.composer
|
||||
RUN chown -R $(whoami):$(whoami) /var/log/
|
||||
RUN chmod -R 775 /var/log
|
||||
|
||||
# Cleanup manually generated build files
|
||||
RUN rm -rf /var/www/public/build
|
||||
RUN npm config set user 0
|
||||
RUN npm config set unsafe-perm true
|
||||
# Frontend bulding
|
||||
RUN sed -i 's/DB_CONNECTION=mysql/DB_CONNECTION=sqlite/g' /var/www/.env
|
||||
RUN sed -i 's/DB_DATABASE=crater/DB_DATABASE=\/tmp\/crater.sqlite/g' /var/www/.env
|
||||
RUN touch /tmp/crater.sqlite
|
||||
RUN composer install --no-interaction --prefer-dist
|
||||
RUN npm i -f
|
||||
RUN npm install --save-dev sass
|
||||
RUN export NODE_OPTIONS="--max-old-space-size=4096" && /usr/bin/npx vite build --target=es2020
|
||||
RUN sed -i 's/DB_CONNECTION=sqlite/DB_CONNECTION=mysql/g' /var/www/.env
|
||||
RUN sed -i 's/DB_DATABASE=\/tmp\/crater.sqlite/DB_DATABASE=crater/g' /var/www/.env
|
||||
|
||||
USER crater-user
|
||||
68
uffizzi/crond/Dockerfile
Normal file
68
uffizzi/crond/Dockerfile
Normal file
@ -0,0 +1,68 @@
|
||||
FROM php:8.1-fpm as build
|
||||
|
||||
# Install system dependencies
|
||||
RUN apt-get update && apt-get install -y \
|
||||
git \
|
||||
curl \
|
||||
libpng-dev \
|
||||
libonig-dev \
|
||||
libxml2-dev \
|
||||
zip \
|
||||
unzip \
|
||||
libzip-dev \
|
||||
libmagickwand-dev \
|
||||
mariadb-client
|
||||
|
||||
# Clear cache
|
||||
RUN apt-get clean && rm -rf /var/lib/apt/lists/*
|
||||
|
||||
RUN pecl install imagick \
|
||||
&& docker-php-ext-enable imagick
|
||||
|
||||
# Install PHP extensions
|
||||
RUN docker-php-ext-install pdo_mysql mbstring zip exif pcntl bcmath gd
|
||||
|
||||
# Get latest Composer
|
||||
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
|
||||
|
||||
# Create system user to run Composer and Artisan Commands
|
||||
RUN useradd -G www-data,root -u 1000 -d /home/crater-user crater-user
|
||||
RUN mkdir -p /home/crater-user/.composer && \
|
||||
chown -R crater-user:crater-user /home/crater-user
|
||||
|
||||
# Mounted volumes
|
||||
COPY ./ /var/www
|
||||
COPY ./docker-compose/php/uploads.ini /usr/local/etc/php/conf.d/uploads.ini
|
||||
COPY ./uffizzi/.env.example /var/www/.env
|
||||
|
||||
# Set working directory
|
||||
WORKDIR /var/www
|
||||
|
||||
RUN chown -R crater-user:crater-user ./
|
||||
RUN chmod -R 775 composer.json composer.lock \
|
||||
composer.lock storage/framework/ \
|
||||
storage/logs/ bootstrap/cache/ /home/crater-user/.composer
|
||||
|
||||
RUN composer config --no-plugins allow-plugins.pestphp/pest-plugin true && \
|
||||
composer install --no-interaction --prefer-dist --optimize-autoloader && \
|
||||
php artisan storage:link || true && \
|
||||
php artisan key:generate
|
||||
|
||||
FROM php:8.0-fpm-alpine
|
||||
|
||||
RUN apk add --no-cache \
|
||||
php8-bcmath
|
||||
|
||||
RUN docker-php-ext-install pdo pdo_mysql bcmath
|
||||
|
||||
COPY docker-compose/crontab /etc/crontabs/root
|
||||
|
||||
# Mounted volumes
|
||||
COPY --from=build /var/www /var/www
|
||||
|
||||
RUN chown -R $(whoami):$(whoami) /var/www/
|
||||
RUN chmod -R 775 /var/www/
|
||||
RUN chown -R $(whoami):$(whoami) /var/log/
|
||||
RUN chmod -R 775 /var/log/
|
||||
|
||||
CMD ["crond", "-f"]
|
||||
58
uffizzi/docker-compose.uffizzi.yml
Normal file
58
uffizzi/docker-compose.uffizzi.yml
Normal file
@ -0,0 +1,58 @@
|
||||
version: '3'
|
||||
|
||||
x-uffizzi:
|
||||
ingress:
|
||||
service: nginx
|
||||
port: 80
|
||||
|
||||
services:
|
||||
app:
|
||||
image: "${APP_IMAGE}"
|
||||
restart: unless-stopped
|
||||
working_dir: /var/www/
|
||||
command: ["-c","
|
||||
composer config --no-plugins allow-plugins.pestphp/pest-plugin true &&
|
||||
composer install --no-interaction --prefer-dist --optimize-autoloader &&
|
||||
php artisan storage:link || true &&
|
||||
php artisan key:generate --force &&
|
||||
php-fpm",
|
||||
]
|
||||
entrypoint: /bin/sh
|
||||
depends_on:
|
||||
- db
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
memory: 1000m
|
||||
|
||||
db:
|
||||
image: mariadb
|
||||
restart: always
|
||||
environment:
|
||||
MYSQL_USER: crater
|
||||
MYSQL_PASSWORD: crater
|
||||
MYSQL_DATABASE: crater
|
||||
MYSQL_ROOT_PASSWORD: crater
|
||||
ports:
|
||||
- '33006:3306'
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
memory: 500m
|
||||
|
||||
nginx:
|
||||
image: "${NGINX_IMAGE}"
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- 80:80
|
||||
depends_on:
|
||||
- app
|
||||
resources:
|
||||
limits:
|
||||
memory: 500m
|
||||
|
||||
cron:
|
||||
image: "${CROND_IMAGE}"
|
||||
restart: always
|
||||
|
||||
|
||||
9
uffizzi/nginx/Dockerfile
Normal file
9
uffizzi/nginx/Dockerfile
Normal file
@ -0,0 +1,9 @@
|
||||
ARG BASE_IMAGE
|
||||
|
||||
FROM $BASE_IMAGE as build
|
||||
FROM nginx:1.17-alpine
|
||||
|
||||
RUN rm /etc/nginx/conf.d/default.conf
|
||||
|
||||
COPY --from=build /var/www /var/www
|
||||
COPY ./uffizzi/nginx/nginx /etc/nginx/conf.d/
|
||||
22
uffizzi/nginx/nginx/nginx.conf
Normal file
22
uffizzi/nginx/nginx/nginx.conf
Normal file
@ -0,0 +1,22 @@
|
||||
server {
|
||||
client_max_body_size 64M;
|
||||
listen 80;
|
||||
index index.php index.html;
|
||||
error_log /var/log/nginx/error.log;
|
||||
access_log /var/log/nginx/access.log;
|
||||
root /var/www/public;
|
||||
location ~ \.php$ {
|
||||
try_files $uri =404;
|
||||
fastcgi_split_path_info ^(.+\.php)(/.+)$;
|
||||
fastcgi_pass localhost:9000;
|
||||
fastcgi_index index.php;
|
||||
include fastcgi_params;
|
||||
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
||||
fastcgi_param PATH_INFO $fastcgi_path_info;
|
||||
fastcgi_read_timeout 300;
|
||||
}
|
||||
location / {
|
||||
try_files $uri $uri/ /index.php?$query_string;
|
||||
gzip_static on;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user