2
0
mirror of https://github.com/frappe/books.git synced 2024-12-23 03:19:01 +00:00

Merge pull request #482 from 18alantom/attachments

feat: Attachments
This commit is contained in:
Alan 2022-10-13 05:04:38 -07:00 committed by GitHub
commit 4ddf2bf81e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 376 additions and 116 deletions

View File

@ -21,8 +21,7 @@ export const sqliteTypeMap: Record<string, KnexColumnType> = {
DynamicLink: 'text',
Password: 'text',
Select: 'text',
File: 'binary',
Attach: 'text',
Attachment: 'text',
AttachImage: 'text',
Color: 'text',
};

View File

@ -7,7 +7,7 @@ import { Money } from 'pesa';
import { Field, FieldTypeEnum, RawValue, TargetField } from 'schemas/types';
import { getIsNullOrUndef } from 'utils';
import { DatabaseHandler } from './dbHandler';
import { DocValue, DocValueMap, RawValueMap } from './types';
import { Attachment, DocValue, DocValueMap, RawValueMap } from './types';
/**
* # Converter
@ -71,6 +71,8 @@ export class Converter {
return toDocFloat(value, field);
case FieldTypeEnum.Check:
return toDocCheck(value, field);
case FieldTypeEnum.Attachment:
return toDocAttachment(value, field);
default:
return toDocString(value, field);
}
@ -92,6 +94,8 @@ export class Converter {
return toRawCheck(value, field);
case FieldTypeEnum.Link:
return toRawLink(value, field);
case FieldTypeEnum.Attachment:
return toRawAttachment(value, field);
default:
return toRawString(value, field);
}
@ -273,6 +277,24 @@ function toDocCheck(value: RawValue, field: Field): boolean {
throwError(value, field, 'doc');
}
function toDocAttachment(value: RawValue, field: Field): null | Attachment {
if (!value) {
return null;
}
if (typeof value !== 'string') {
console.log('being thrown doc1', typeof value, value);
throwError(value, field, 'doc');
}
try {
return JSON.parse(value) || null;
} catch {
console.log('being thrown doc2', typeof value, value);
throwError(value, field, 'doc');
}
}
function toRawCurrency(value: DocValue, fyo: Fyo, field: Field): string {
if (isPesa(value)) {
return (value as Money).store;
@ -394,6 +416,23 @@ function toRawLink(value: DocValue, field: Field): string | null {
throwError(value, field, 'raw');
}
function toRawAttachment(value: DocValue, field: Field): null | string {
if (!value) {
return null;
}
if (
(value as Attachment)?.name &&
(value as Attachment)?.data &&
(value as Attachment)?.type
) {
return JSON.stringify(value);
}
console.log('being thrown raw', typeof value, value);
throwError(value, field, 'raw');
}
function throwError<T>(value: T, field: Field, type: 'raw' | 'doc'): never {
throw new ValueError(
`invalid ${type} conversion '${value}' of type ${typeof value} found, field: ${JSON.stringify(

View File

@ -6,7 +6,7 @@ import Observable from 'fyo/utils/observable';
import { Schema } from 'schemas/types';
import { getRandomString } from 'utils';
import { Fyo } from '..';
import { DocValueMap } from './types';
import { DocValueMap, RawValueMap } from './types';
export class DocHandler {
fyo: Fyo;
@ -79,10 +79,11 @@ export class DocHandler {
getNewDoc(
schemaName: string,
data: DocValueMap = {},
data: DocValueMap | RawValueMap = {},
cacheDoc: boolean = true,
schema?: Schema,
Model?: typeof Doc
Model?: typeof Doc,
isRawValueMap: boolean = true
): Doc {
if (!this.models[schemaName] && Model) {
this.models[schemaName] = Model;
@ -95,7 +96,7 @@ export class DocHandler {
throw new NotFoundError(`Schema not found for ${schemaName}`);
}
const doc = new Model!(schema, data, this.fyo);
const doc = new Model!(schema, data, this.fyo, isRawValueMap);
doc.name ??= getRandomString();
if (cacheDoc) {
this.#addToCache(doc);

View File

@ -4,6 +4,7 @@ import { RawValue } from 'schemas/types';
import { AuthDemuxBase } from 'utils/auth/types';
import { DatabaseDemuxBase } from 'utils/db/types';
export type Attachment = { name: string; type: string; data: string };
export type DocValue =
| string
| number
@ -11,6 +12,7 @@ export type DocValue =
| Date
| Money
| null
| Attachment
| undefined;
export type DocValueMap = Record<string, DocValue | Doc[] | DocValueMap[]>;
export type RawValueMap = Record<string, RawValue | RawValueMap[]>;

View File

@ -1,6 +1,6 @@
import { Fyo } from 'fyo';
import { Converter } from 'fyo/core/converter';
import { DocValue, DocValueMap } from 'fyo/core/types';
import { DocValue, DocValueMap, RawValueMap } from 'fyo/core/types';
import { Verb } from 'fyo/telemetry/types';
import { DEFAULT_USER } from 'fyo/utils/consts';
import { ConflictError, MandatoryError, NotFoundError } from 'fyo/utils/errors';
@ -66,7 +66,12 @@ export class Doc extends Observable<DocValue | Doc[]> {
_notInserted: boolean = true;
_syncing = false;
constructor(schema: Schema, data: DocValueMap, fyo: Fyo) {
constructor(
schema: Schema,
data: DocValueMap,
fyo: Fyo,
convertToDocValue: boolean = true
) {
super();
this.fyo = markRaw(fyo);
this.schema = schema;
@ -77,7 +82,7 @@ export class Doc extends Observable<DocValue | Doc[]> {
}
this._setDefaults();
this._setValuesWithoutChecks(data);
this._setValuesWithoutChecks(data, convertToDocValue);
}
get schemaName(): string {
@ -152,7 +157,7 @@ export class Doc extends Observable<DocValue | Doc[]> {
return false;
}
_setValuesWithoutChecks(data: DocValueMap) {
_setValuesWithoutChecks(data: DocValueMap, convertToDocValue: boolean) {
for (const field of this.schema.fields) {
const fieldname = field.fieldname;
const value = data[field.fieldname];
@ -161,6 +166,8 @@ export class Doc extends Observable<DocValue | Doc[]> {
for (const row of value) {
this.push(fieldname, row);
}
} else if (value !== undefined && !convertToDocValue) {
this[fieldname] = value;
} else if (value !== undefined) {
this[fieldname] = Converter.toDocValue(
value as RawValue,
@ -362,6 +369,9 @@ export class Doc extends Observable<DocValue | Doc[]> {
const childDoc = this.fyo.doc.getNewDoc(
childSchemaName,
docValueMap,
false,
undefined,
undefined,
false
);
childDoc.parentdoc = this;
@ -578,7 +588,7 @@ export class Doc extends Observable<DocValue | Doc[]> {
async _syncValues(data: DocValueMap) {
this._clearValues();
this._setValuesWithoutChecks(data);
this._setValuesWithoutChecks(data, false);
await this._setComputedValuesFromFormulas();
this._dirty = false;
this.trigger('change', {
@ -911,7 +921,12 @@ export class Doc extends Observable<DocValue | Doc[]> {
updateMap.name = updateMap.name + ' CPY';
}
return this.fyo.doc.getNewDoc(this.schemaName, updateMap);
const rawUpdateMap = this.fyo.db.converter.toRawValueMap(
this.schemaName,
updateMap
) as RawValueMap;
return this.fyo.doc.getNewDoc(this.schemaName, rawUpdateMap, true);
}
/**

View File

@ -543,6 +543,9 @@ export class Payment extends Transactional {
referenceId: () => this.paymentMethod === 'Cash',
clearanceDate: () => this.paymentMethod === 'Cash',
amountPaid: () => this.writeoff?.isZero() ?? true,
attachment: () =>
!(this.attachment || !(this.isSubmitted || this.isCancelled)),
for: () => !((this.isSubmitted || this.isCancelled) && this.for?.length),
};
static filters: FiltersMap = {

View File

@ -30,7 +30,7 @@
"luxon": "^2.0.2",
"node-fetch": "2",
"pesa": "^1.1.12",
"vue": "^3.2.36",
"vue": "^3.2.40",
"vue-router": "^4.0.12"
},
"devDependencies": {

View File

@ -125,6 +125,12 @@
"label": "Notes",
"placeholder": "Add invoice terms",
"fieldtype": "Text"
},
{
"fieldname": "attachment",
"placeholder": "Add attachment",
"label": "Attachment",
"fieldtype": "Attachment"
}
],
"keywordFields": ["name", "party"]

View File

@ -101,6 +101,12 @@
"create": true,
"required": true,
"default": "JV-"
},
{
"fieldname": "attachment",
"placeholder": "Add attachment",
"label": "Attachment",
"fieldtype": "Attachment"
}
],
"keywordFields": ["name", "entryType"]

View File

@ -133,6 +133,12 @@
"fieldtype": "Table",
"target": "PaymentFor",
"required": false
},
{
"fieldname": "attachment",
"placeholder": "Add attachment",
"label": "Attachment",
"fieldtype": "Attachment"
}
],
"quickEditFields": [
@ -149,6 +155,7 @@
"amount",
"writeoff",
"amountPaid",
"attachment",
"for"
],
"keywordFields": ["name", "party", "paymentType"]

View File

@ -14,6 +14,7 @@ export enum FieldTypeEnum {
Currency = 'Currency',
Text = 'Text',
Color = 'Color',
Attachment = 'Attachment',
}
export type FieldType = keyof typeof FieldTypeEnum;

View File

@ -61,6 +61,7 @@
<script>
import { ipcRenderer } from 'electron';
import { fyo } from 'src/initFyo';
import { getDataURL } from 'src/utils/misc';
import { IPC_ACTIONS } from 'utils/messages';
import Base from './Base';
@ -96,21 +97,10 @@ export default {
return;
}
const dataURL = await this.getDataURL(name, data);
this.triggerChange(dataURL);
},
getDataURL(name, data) {
const extension = name.split('.').at(-1);
const blob = new Blob([data], { type: 'image/' + extension });
return new Promise((resolve) => {
const fr = new FileReader();
fr.addEventListener('loadend', () => {
resolve(fr.result);
});
fr.readAsDataURL(blob);
});
const type = 'image/' + extension;
const dataURL = await getDataURL(type, data);
this.triggerChange(dataURL);
},
},
};

View File

@ -0,0 +1,133 @@
<template>
<div :class="containerClasses" class="flex gap-2 items-center">
<label
for="attachment"
class="block whitespace-nowrap overflow-auto no-scrollbar"
:class="[inputClasses, !value ? 'text-gray-600' : 'cursor-default']"
>{{ label }}</label
>
<input
ref="fileInput"
id="attachment"
type="file"
accept="image/*,.pdf"
class="hidden"
:disabled="!!value"
@input="selectFile"
/>
<!-- Buttons -->
<div class="mr-2 flex gap-2">
<!-- Upload Button -->
<button v-if="!value" class="bg-gray-300 p-0.5 rounded" @click="upload">
<FeatherIcon name="upload" class="h-4 w-4 text-gray-600" />
</button>
<!-- Download Button -->
<button v-if="value" class="bg-gray-300 p-0.5 rounded" @click="download">
<FeatherIcon name="download" class="h-4 w-4 text-gray-600" />
</button>
<!-- Clear Button -->
<button
v-if="value && !isReadOnly"
class="bg-gray-300 p-0.5 rounded"
@click="clear"
>
<FeatherIcon name="x" class="h-4 w-4 text-gray-600" />
</button>
</div>
</div>
</template>
<script lang="ts">
import { t } from 'fyo';
import { Attachment } from 'fyo/core/types';
import { Field } from 'schemas/types';
import { convertFileToDataURL } from 'src/utils/misc';
import { defineComponent, PropType } from 'vue';
import FeatherIcon from '../FeatherIcon.vue';
import Base from './Base.vue';
export default defineComponent({
extends: Base,
props: {
df: Object as PropType<Field>,
value: { type: Object as PropType<Attachment | null>, default: null },
border: { type: Boolean, default: false },
size: String,
},
methods: {
upload() {
(this.$refs.fileInput as HTMLInputElement).click();
},
clear() {
// @ts-ignore
this.triggerChange(null);
},
download() {
if (!this.value) {
return;
}
const { name, data } = this.value;
if (!name || !data) {
return;
}
const a = document.createElement('a') as HTMLAnchorElement;
a.style.display = 'none';
a.href = data;
a.target = '_self';
a.download = name;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
},
async selectFile(e: Event) {
const target = e.target as HTMLInputElement;
const file = target.files?.[0];
if (!file) {
return;
}
const attachment = await this.getAttachment(file);
// @ts-ignore
this.triggerChange(attachment);
},
async getAttachment(file: File | null) {
if (!file) {
return null;
}
const name = file.name;
const type = file.type;
const data = await convertFileToDataURL(file, type);
return { name, type, data };
},
},
computed: {
label() {
if (this.value) {
return this.value.name;
}
return this.df?.placeholder ?? this.df?.label ?? t`Attachment`;
},
inputReadOnlyClasses() {
if (!this.value) {
return 'text-gray-600';
} else if (this.isReadOnly) {
return 'text-gray-800 cursor-default';
}
return 'text-gray-900';
},
containerReadOnlyClasses() {
return '';
},
},
components: { FeatherIcon },
});
</script>

View File

@ -72,46 +72,66 @@ export default {
* These classes will be used by components that extend Base
*/
const classes = [
const classes = [];
classes.push(this.baseInputClasses);
if (this.textRight ?? isNumeric(this.df)) {
classes.push('text-right');
}
classes.push(this.sizeClasses);
classes.push(this.inputReadOnlyClasses);
return this.getInputClassesFromProp(classes).filter(Boolean);
},
baseInputClasses() {
return [
'text-base',
'focus:outline-none',
'w-full',
'placeholder-gray-500',
];
if (this.textRight ?? isNumeric(this.df)) {
classes.push('text-right');
}
},
sizeClasses() {
if (this.size === 'small') {
classes.push('px-2 py-1');
} else {
classes.push('px-3 py-2');
return 'px-2 py-1';
}
return 'px-3 py-2';
},
inputReadOnlyClasses() {
if (this.isReadOnly) {
classes.push('text-gray-800 cursor-default');
} else {
classes.push('text-gray-900');
return 'text-gray-800 cursor-default';
}
return this.getInputClassesFromProp(classes);
return 'text-gray-900';
},
containerClasses() {
/**
* Used to accomodate extending compoents where the input is contained in
* a div eg AutoComplete
*/
const classes = ['rounded'];
const classes = [];
classes.push(this.baseContainerClasses);
classes.push(this.containerReadOnlyClasses);
classes.push(this.borderClasses);
return classes.filter(Boolean);
},
baseContainerClasses() {
return ['rounded'];
},
containerReadOnlyClasses() {
if (!this.isReadOnly) {
classes.push('focus-within:bg-gray-100');
return 'focus-within:bg-gray-100';
}
return '';
},
borderClasses() {
if (this.border) {
classes.push('bg-gray-50 border border-gray-200');
return 'bg-gray-50 border border-gray-200';
}
return classes;
return '';
},
inputPlaceholder() {
return this.placeholder || this.df.placeholder || this.df.label;

View File

@ -1,6 +1,7 @@
<script>
import { h } from 'vue';
import AttachImage from './AttachImage.vue';
import Attachment from './Attachment.vue';
import AutoComplete from './AutoComplete.vue';
import Check from './Check.vue';
import Color from './Color.vue';
@ -28,6 +29,7 @@ const components = {
DynamicLink,
Int,
Float,
Attachment,
Currency,
Text,
};

View File

@ -34,7 +34,7 @@ export default {
}
if (this.ratio.length) {
obj['grid-template-columns'] = this.ratio
.map((r) => `${r}fr`)
.map((r) => `minmax(0, ${r}fr)`)
.join(' ');
}
if (this.gridTemplateColumns) {

View File

@ -307,7 +307,7 @@ export default {
computed: {
style() {
let templateColumns = (this.columnRatio || [1, 1])
.map((r) => `${r}fr`)
.map((r) => `minmax(0, ${r}fr)`)
.join(' ');
return {
'grid-template-columns': templateColumns,

View File

@ -104,6 +104,14 @@
@change="(value) => doc.set('account', value)"
:read-only="doc?.submitted"
/>
<FormControl
v-if="doc.attachment || !(doc.isSubmitted || doc.isCancelled)"
:border="true"
:df="getField('attachment')"
:value="doc.attachment"
@change="(value) => doc.set('attachment', value)"
:read-only="doc?.submitted"
/>
</div>
<hr />

View File

@ -85,6 +85,14 @@
@change="(value) => doc.set('referenceDate', value)"
:read-only="doc.submitted"
/>
<FormControl
v-if="doc.attachment || !(doc.isSubmitted || doc.isCancelled)"
:border="true"
:df="getField('attachment')"
:value="doc.attachment"
@change="(value) => doc.set('attachment', value)"
:read-only="doc?.submitted"
/>
</div>
<hr />

View File

@ -76,6 +76,7 @@
<!-- Rest of the form -->
<TwoColumnForm
class="w-full"
ref="form"
v-if="doc"
:doc="doc"

View File

@ -110,3 +110,22 @@ export const docsPathMap: Record<string, string | undefined> = {
Settings: 'miscellaneous/settings',
ChartOfAccounts: 'miscellaneous/chart-of-accounts',
};
export async function getDataURL(type: string, data: Uint8Array) {
const blob = new Blob([data], { type });
return new Promise<string>((resolve) => {
const fr = new FileReader();
fr.addEventListener('loadend', () => {
resolve(fr.result as string);
});
fr.readAsDataURL(blob);
});
}
export async function convertFileToDataURL(file: File, type: string) {
const buffer = await file.arrayBuffer();
const array = new Uint8Array(buffer);
return await getDataURL(type, array);
}

136
yarn.lock
View File

@ -2029,47 +2029,47 @@
semver "^7.3.4"
strip-ansi "^6.0.0"
"@vue/compiler-core@3.2.36":
version "3.2.36"
resolved "https://registry.yarnpkg.com/@vue/compiler-core/-/compiler-core-3.2.36.tgz#2fa44595308c95610602df54dcb69063ba2c8383"
integrity sha512-bbyZM5hvBicv0PW3KUfVi+x3ylHnfKG7DOn5wM+f2OztTzTjLEyBb/5yrarIYpmnGitVGbjZqDbODyW4iK8hqw==
"@vue/compiler-core@3.2.40":
version "3.2.40"
resolved "https://registry.yarnpkg.com/@vue/compiler-core/-/compiler-core-3.2.40.tgz#c785501f09536748121e937fb87605bbb1ada8e5"
integrity sha512-2Dc3Stk0J/VyQ4OUr2yEC53kU28614lZS+bnrCbFSAIftBJ40g/2yQzf4mPBiFuqguMB7hyHaujdgZAQ67kZYA==
dependencies:
"@babel/parser" "^7.16.4"
"@vue/shared" "3.2.36"
"@vue/shared" "3.2.40"
estree-walker "^2.0.2"
source-map "^0.6.1"
"@vue/compiler-dom@3.2.36":
version "3.2.36"
resolved "https://registry.yarnpkg.com/@vue/compiler-dom/-/compiler-dom-3.2.36.tgz#16d911ff163ed5fc8087a01645bf14bb7f325401"
integrity sha512-tcOTAOiW4s24QLnq+ON6J+GRONXJ+A/mqKCORi0LSlIh8XQlNnlm24y8xIL8la+ZDgkdbjarQ9ZqYSvEja6gVA==
"@vue/compiler-dom@3.2.40":
version "3.2.40"
resolved "https://registry.yarnpkg.com/@vue/compiler-dom/-/compiler-dom-3.2.40.tgz#c225418773774db536174d30d3f25ba42a33e7e4"
integrity sha512-OZCNyYVC2LQJy4H7h0o28rtk+4v+HMQygRTpmibGoG9wZyomQiS5otU7qo3Wlq5UfHDw2RFwxb9BJgKjVpjrQw==
dependencies:
"@vue/compiler-core" "3.2.36"
"@vue/shared" "3.2.36"
"@vue/compiler-core" "3.2.40"
"@vue/shared" "3.2.40"
"@vue/compiler-sfc@3.2.36":
version "3.2.36"
resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-3.2.36.tgz#e5065e7c0e5170ffa750e3c3dd93a29db109d0f2"
integrity sha512-AvGb4bTj4W8uQ4BqaSxo7UwTEqX5utdRSMyHy58OragWlt8nEACQ9mIeQh3K4di4/SX+41+pJrLIY01lHAOFOA==
"@vue/compiler-sfc@3.2.40":
version "3.2.40"
resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-3.2.40.tgz#61823283efc84d25d9d2989458f305d32a2ed141"
integrity sha512-tzqwniIN1fu1PDHC3CpqY/dPCfN/RN1thpBC+g69kJcrl7mbGiHKNwbA6kJ3XKKy8R6JLKqcpVugqN4HkeBFFg==
dependencies:
"@babel/parser" "^7.16.4"
"@vue/compiler-core" "3.2.36"
"@vue/compiler-dom" "3.2.36"
"@vue/compiler-ssr" "3.2.36"
"@vue/reactivity-transform" "3.2.36"
"@vue/shared" "3.2.36"
"@vue/compiler-core" "3.2.40"
"@vue/compiler-dom" "3.2.40"
"@vue/compiler-ssr" "3.2.40"
"@vue/reactivity-transform" "3.2.40"
"@vue/shared" "3.2.40"
estree-walker "^2.0.2"
magic-string "^0.25.7"
postcss "^8.1.10"
source-map "^0.6.1"
"@vue/compiler-ssr@3.2.36":
version "3.2.36"
resolved "https://registry.yarnpkg.com/@vue/compiler-ssr/-/compiler-ssr-3.2.36.tgz#314f3a9424db58142c3608f48cbda7aa05fc66cb"
integrity sha512-+KugInUFRvOxEdLkZwE+W43BqHyhBh0jpYXhmqw1xGq2dmE6J9eZ8UUSOKNhdHtQ/iNLWWeK/wPZkVLUf3YGaw==
"@vue/compiler-ssr@3.2.40":
version "3.2.40"
resolved "https://registry.yarnpkg.com/@vue/compiler-ssr/-/compiler-ssr-3.2.40.tgz#67df95a096c63e9ec4b50b84cc6f05816793629c"
integrity sha512-80cQcgasKjrPPuKcxwuCx7feq+wC6oFl5YaKSee9pV3DNq+6fmCVwEEC3vvkf/E2aI76rIJSOYHsWSEIxK74oQ==
dependencies:
"@vue/compiler-dom" "3.2.36"
"@vue/shared" "3.2.36"
"@vue/compiler-dom" "3.2.40"
"@vue/shared" "3.2.40"
"@vue/component-compiler-utils@^3.1.0", "@vue/component-compiler-utils@^3.1.2":
version "3.3.0"
@ -2104,53 +2104,53 @@
resolved "https://registry.yarnpkg.com/@vue/preload-webpack-plugin/-/preload-webpack-plugin-1.1.2.tgz#ceb924b4ecb3b9c43871c7a429a02f8423e621ab"
integrity sha512-LIZMuJk38pk9U9Ur4YzHjlIyMuxPlACdBIHH9/nGYVTsaGKOSnSuELiE8vS9wa+dJpIYspYUOqk+L1Q4pgHQHQ==
"@vue/reactivity-transform@3.2.36":
version "3.2.36"
resolved "https://registry.yarnpkg.com/@vue/reactivity-transform/-/reactivity-transform-3.2.36.tgz#8426a941b0b09d1b94fc162d4642758183b5d133"
integrity sha512-Jk5o2BhpODC9XTA7o4EL8hSJ4JyrFWErLtClG3NH8wDS7ri9jBDWxI7/549T7JY9uilKsaNM+4pJASLj5dtRwA==
"@vue/reactivity-transform@3.2.40":
version "3.2.40"
resolved "https://registry.yarnpkg.com/@vue/reactivity-transform/-/reactivity-transform-3.2.40.tgz#dc24b9074b26f0d9dd2034c6349f5bb2a51c86ac"
integrity sha512-HQUCVwEaacq6fGEsg2NUuGKIhUveMCjOk8jGHqLXPI2w6zFoPrlQhwWEaINTv5kkZDXKEnCijAp+4gNEHG03yw==
dependencies:
"@babel/parser" "^7.16.4"
"@vue/compiler-core" "3.2.36"
"@vue/shared" "3.2.36"
"@vue/compiler-core" "3.2.40"
"@vue/shared" "3.2.40"
estree-walker "^2.0.2"
magic-string "^0.25.7"
"@vue/reactivity@3.2.36":
version "3.2.36"
resolved "https://registry.yarnpkg.com/@vue/reactivity/-/reactivity-3.2.36.tgz#026b14e716febffe80cd284fd8a2b33378968646"
integrity sha512-c2qvopo0crh9A4GXi2/2kfGYMxsJW4tVILrqRPydVGZHhq0fnzy6qmclWOhBFckEhmyxmpHpdJtIRYGeKcuhnA==
"@vue/reactivity@3.2.40":
version "3.2.40"
resolved "https://registry.yarnpkg.com/@vue/reactivity/-/reactivity-3.2.40.tgz#ae65496f5b364e4e481c426f391568ed7d133cca"
integrity sha512-N9qgGLlZmtUBMHF9xDT4EkD9RdXde1Xbveb+niWMXuHVWQP5BzgRmE3SFyUBBcyayG4y1lhoz+lphGRRxxK4RA==
dependencies:
"@vue/shared" "3.2.36"
"@vue/shared" "3.2.40"
"@vue/runtime-core@3.2.36":
version "3.2.36"
resolved "https://registry.yarnpkg.com/@vue/runtime-core/-/runtime-core-3.2.36.tgz#be5115e665679c26bf3807d2326675dc1d847134"
integrity sha512-PTWBD+Lub+1U3/KhbCExrfxyS14hstLX+cBboxVHaz+kXoiDLNDEYAovPtxeTutbqtClIXtft+wcGdC+FUQ9qQ==
"@vue/runtime-core@3.2.40":
version "3.2.40"
resolved "https://registry.yarnpkg.com/@vue/runtime-core/-/runtime-core-3.2.40.tgz#e814358bf1b0ff6d4a6b4f8f62d9f341964fb275"
integrity sha512-U1+rWf0H8xK8aBUZhnrN97yoZfHbjgw/bGUzfgKPJl69/mXDuSg8CbdBYBn6VVQdR947vWneQBFzdhasyzMUKg==
dependencies:
"@vue/reactivity" "3.2.36"
"@vue/shared" "3.2.36"
"@vue/reactivity" "3.2.40"
"@vue/shared" "3.2.40"
"@vue/runtime-dom@3.2.36":
version "3.2.36"
resolved "https://registry.yarnpkg.com/@vue/runtime-dom/-/runtime-dom-3.2.36.tgz#cd5d403ea23c18ee7c17767103a1b2f8263c54bb"
integrity sha512-gYPYblm7QXHVuBohqNRRT7Wez0f2Mx2D40rb4fleehrJU9CnkjG0phhcGEZFfGwCmHZRqBCRgbFWE98bPULqkg==
"@vue/runtime-dom@3.2.40":
version "3.2.40"
resolved "https://registry.yarnpkg.com/@vue/runtime-dom/-/runtime-dom-3.2.40.tgz#975119feac5ab703aa9bbbf37c9cc966602c8eab"
integrity sha512-AO2HMQ+0s2+MCec8hXAhxMgWhFhOPJ/CyRXnmTJ6XIOnJFLrH5Iq3TNwvVcODGR295jy77I6dWPj+wvFoSYaww==
dependencies:
"@vue/runtime-core" "3.2.36"
"@vue/shared" "3.2.36"
"@vue/runtime-core" "3.2.40"
"@vue/shared" "3.2.40"
csstype "^2.6.8"
"@vue/server-renderer@3.2.36":
version "3.2.36"
resolved "https://registry.yarnpkg.com/@vue/server-renderer/-/server-renderer-3.2.36.tgz#1e7c1cf63bd17df7828d04e8c780ee6ca7a9ed7c"
integrity sha512-uZE0+jfye6yYXWvAQYeHZv+f50sRryvy16uiqzk3jn8hEY8zTjI+rzlmZSGoE915k+W/Ol9XSw6vxOUD8dGkUg==
"@vue/server-renderer@3.2.40":
version "3.2.40"
resolved "https://registry.yarnpkg.com/@vue/server-renderer/-/server-renderer-3.2.40.tgz#55eaac31f7105c3907e1895129bf4efb6b0ce393"
integrity sha512-gtUcpRwrXOJPJ4qyBpU3EyxQa4EkV8I4f8VrDePcGCPe4O/hd0BPS7v9OgjIQob6Ap8VDz9G+mGTKazE45/95w==
dependencies:
"@vue/compiler-ssr" "3.2.36"
"@vue/shared" "3.2.36"
"@vue/compiler-ssr" "3.2.40"
"@vue/shared" "3.2.40"
"@vue/shared@3.2.36":
version "3.2.36"
resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.2.36.tgz#35e11200542cf29068ba787dad57da9bdb82f644"
integrity sha512-JtB41wXl7Au3+Nl3gD16Cfpj7k/6aCroZ6BbOiCMFCMvrOpkg/qQUXTso2XowaNqBbnkuGHurLAqkLBxNGc1hQ==
"@vue/shared@3.2.40":
version "3.2.40"
resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.2.40.tgz#e57799da2a930b975321981fcee3d1e90ed257ae"
integrity sha512-0PLQ6RUtZM0vO3teRfzGi4ltLUO5aO+kLgwh4Um3THSR03rpQWLTuRCkuO5A41ITzwdWeKdPHtSARuPkoo5pCQ==
"@vue/web-component-wrapper@^1.2.0":
version "1.3.0"
@ -12533,16 +12533,16 @@ vue-template-es2015-compiler@^1.9.0:
resolved "https://registry.yarnpkg.com/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.9.1.tgz#1ee3bc9a16ecbf5118be334bb15f9c46f82f5825"
integrity sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==
vue@^3.2.36:
version "3.2.36"
resolved "https://registry.yarnpkg.com/vue/-/vue-3.2.36.tgz#8daa996e2ced521708de97d066c7c998e8bc3378"
integrity sha512-5yTXmrE6gW8IQgttzHW5bfBiFA6mx35ZXHjGLDmKYzW6MMmYvCwuKybANRepwkMYeXw2v1buGg3/lPICY5YlZw==
vue@^3.2.40:
version "3.2.40"
resolved "https://registry.yarnpkg.com/vue/-/vue-3.2.40.tgz#23f387f6f9b3a0767938db6751e4fb5900f0ee34"
integrity sha512-1mGHulzUbl2Nk3pfvI5aXYYyJUs1nm4kyvuz38u4xlQkLUn1i2R7nDbI4TufECmY8v1qNBHYy62bCaM+3cHP2A==
dependencies:
"@vue/compiler-dom" "3.2.36"
"@vue/compiler-sfc" "3.2.36"
"@vue/runtime-dom" "3.2.36"
"@vue/server-renderer" "3.2.36"
"@vue/shared" "3.2.36"
"@vue/compiler-dom" "3.2.40"
"@vue/compiler-sfc" "3.2.40"
"@vue/runtime-dom" "3.2.40"
"@vue/server-renderer" "3.2.40"
"@vue/shared" "3.2.40"
watchpack-chokidar2@^2.0.1:
version "2.0.1"