🎯 MapView v2.0 - Global Deployment Ready

 MAJOR FEATURES:
• Auto-zoom intelligence với smart bounds fitting
• Enhanced 3D GPS markers với pulsing effects
• Professional route display với 6-layer rendering
• Status-based parking icons với availability indicators
• Production-ready build optimizations

🗺️ AUTO-ZOOM FEATURES:
• Smart bounds fitting cho GPS + selected parking
• Adaptive padding (50px) cho visual balance
• Max zoom control (level 16) để tránh quá gần
• Dynamic centering khi không có selection

🎨 ENHANCED VISUALS:
• 3D GPS marker với multi-layer pulse effects
• Advanced parking icons với status colors
• Selection highlighting với animation
• Dimming system cho non-selected items

🛣️ ROUTE SYSTEM:
• OpenRouteService API integration
• Multi-layer route rendering (glow, shadow, main, animated)
• Real-time distance & duration calculation
• Visual route info trong popup

📱 PRODUCTION READY:
• SSR safe với dynamic imports
• Build errors resolved
• Global deployment via Vercel
• Optimized performance

🌍 DEPLOYMENT:
• Vercel: https://whatever-ctk2auuxr-phong12hexdockworks-projects.vercel.app
• Bundle size: 22.8 kB optimized
• Global CDN distribution
• HTTPS enabled

💾 VERSION CONTROL:
• MapView-v2.0.tsx backup created
• MAPVIEW_VERSIONS.md documentation
• Full version history tracking
This commit is contained in:
2025-07-20 19:52:16 +07:00
parent 3203463a6a
commit c65cc97a33
64624 changed files with 7199453 additions and 6462 deletions

View File

@@ -0,0 +1 @@
export declare const MULTER_MODULE_OPTIONS = "MULTER_MODULE_OPTIONS";

View File

@@ -0,0 +1,4 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.MULTER_MODULE_OPTIONS = void 0;
exports.MULTER_MODULE_OPTIONS = 'MULTER_MODULE_OPTIONS';

View File

@@ -0,0 +1,3 @@
export * from './interceptors';
export * from './interfaces';
export * from './multer.module';

View File

@@ -0,0 +1,6 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const tslib_1 = require("tslib");
tslib_1.__exportStar(require("./interceptors"), exports);
tslib_1.__exportStar(require("./interfaces"), exports);
tslib_1.__exportStar(require("./multer.module"), exports);

View File

@@ -0,0 +1,8 @@
import { NestInterceptor, Type } from '@nestjs/common';
import { MulterOptions } from '../interfaces/multer-options.interface';
/**
* @param localOptions
*
* @publicApi
*/
export declare function AnyFilesInterceptor(localOptions?: MulterOptions): Type<NestInterceptor>;

View File

@@ -0,0 +1,41 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.AnyFilesInterceptor = AnyFilesInterceptor;
const tslib_1 = require("tslib");
const common_1 = require("@nestjs/common");
const multer = require("multer");
const files_constants_1 = require("../files.constants");
const multer_utils_1 = require("../multer/multer.utils");
/**
* @param localOptions
*
* @publicApi
*/
function AnyFilesInterceptor(localOptions) {
let MixinInterceptor = class MixinInterceptor {
constructor(options = {}) {
this.multer = multer({
...options,
...localOptions,
});
}
async intercept(context, next) {
const ctx = context.switchToHttp();
await new Promise((resolve, reject) => this.multer.any()(ctx.getRequest(), ctx.getResponse(), (err) => {
if (err) {
const error = (0, multer_utils_1.transformException)(err);
return reject(error);
}
resolve();
}));
return next.handle();
}
};
MixinInterceptor = tslib_1.__decorate([
tslib_1.__param(0, (0, common_1.Optional)()),
tslib_1.__param(0, (0, common_1.Inject)(files_constants_1.MULTER_MODULE_OPTIONS)),
tslib_1.__metadata("design:paramtypes", [Object])
], MixinInterceptor);
const Interceptor = (0, common_1.mixin)(MixinInterceptor);
return Interceptor;
}

View File

@@ -0,0 +1,8 @@
import { NestInterceptor, Type } from '@nestjs/common';
import { MulterField, MulterOptions } from '../interfaces/multer-options.interface';
/**
* @param uploadFields
* @param localOptions
* @publicApi
*/
export declare function FileFieldsInterceptor(uploadFields: MulterField[], localOptions?: MulterOptions): Type<NestInterceptor>;

View File

@@ -0,0 +1,41 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.FileFieldsInterceptor = FileFieldsInterceptor;
const tslib_1 = require("tslib");
const common_1 = require("@nestjs/common");
const multer = require("multer");
const files_constants_1 = require("../files.constants");
const multer_utils_1 = require("../multer/multer.utils");
/**
* @param uploadFields
* @param localOptions
* @publicApi
*/
function FileFieldsInterceptor(uploadFields, localOptions) {
let MixinInterceptor = class MixinInterceptor {
constructor(options = {}) {
this.multer = multer({
...options,
...localOptions,
});
}
async intercept(context, next) {
const ctx = context.switchToHttp();
await new Promise((resolve, reject) => this.multer.fields(uploadFields)(ctx.getRequest(), ctx.getResponse(), (err) => {
if (err) {
const error = (0, multer_utils_1.transformException)(err);
return reject(error);
}
resolve();
}));
return next.handle();
}
};
MixinInterceptor = tslib_1.__decorate([
tslib_1.__param(0, (0, common_1.Optional)()),
tslib_1.__param(0, (0, common_1.Inject)(files_constants_1.MULTER_MODULE_OPTIONS)),
tslib_1.__metadata("design:paramtypes", [Object])
], MixinInterceptor);
const Interceptor = (0, common_1.mixin)(MixinInterceptor);
return Interceptor;
}

View File

@@ -0,0 +1,9 @@
import { NestInterceptor, Type } from '@nestjs/common';
import { MulterOptions } from '../interfaces/multer-options.interface';
/**
* @param fieldName
* @param localOptions
*
* @publicApi
*/
export declare function FileInterceptor(fieldName: string, localOptions?: MulterOptions): Type<NestInterceptor>;

View File

@@ -0,0 +1,42 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.FileInterceptor = FileInterceptor;
const tslib_1 = require("tslib");
const common_1 = require("@nestjs/common");
const multer = require("multer");
const files_constants_1 = require("../files.constants");
const multer_utils_1 = require("../multer/multer.utils");
/**
* @param fieldName
* @param localOptions
*
* @publicApi
*/
function FileInterceptor(fieldName, localOptions) {
let MixinInterceptor = class MixinInterceptor {
constructor(options = {}) {
this.multer = multer({
...options,
...localOptions,
});
}
async intercept(context, next) {
const ctx = context.switchToHttp();
await new Promise((resolve, reject) => this.multer.single(fieldName)(ctx.getRequest(), ctx.getResponse(), (err) => {
if (err) {
const error = (0, multer_utils_1.transformException)(err);
return reject(error);
}
resolve();
}));
return next.handle();
}
};
MixinInterceptor = tslib_1.__decorate([
tslib_1.__param(0, (0, common_1.Optional)()),
tslib_1.__param(0, (0, common_1.Inject)(files_constants_1.MULTER_MODULE_OPTIONS)),
tslib_1.__metadata("design:paramtypes", [Object])
], MixinInterceptor);
const Interceptor = (0, common_1.mixin)(MixinInterceptor);
return Interceptor;
}

View File

@@ -0,0 +1,11 @@
import { NestInterceptor, Type } from '@nestjs/common';
import { MulterOptions } from '../interfaces/multer-options.interface';
/**
*
* @param fieldName
* @param maxCount
* @param localOptions
*
* @publicApi
*/
export declare function FilesInterceptor(fieldName: string, maxCount?: number, localOptions?: MulterOptions): Type<NestInterceptor>;

View File

@@ -0,0 +1,44 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.FilesInterceptor = FilesInterceptor;
const tslib_1 = require("tslib");
const common_1 = require("@nestjs/common");
const multer = require("multer");
const files_constants_1 = require("../files.constants");
const multer_utils_1 = require("../multer/multer.utils");
/**
*
* @param fieldName
* @param maxCount
* @param localOptions
*
* @publicApi
*/
function FilesInterceptor(fieldName, maxCount, localOptions) {
let MixinInterceptor = class MixinInterceptor {
constructor(options = {}) {
this.multer = multer({
...options,
...localOptions,
});
}
async intercept(context, next) {
const ctx = context.switchToHttp();
await new Promise((resolve, reject) => this.multer.array(fieldName, maxCount)(ctx.getRequest(), ctx.getResponse(), (err) => {
if (err) {
const error = (0, multer_utils_1.transformException)(err);
return reject(error);
}
resolve();
}));
return next.handle();
}
};
MixinInterceptor = tslib_1.__decorate([
tslib_1.__param(0, (0, common_1.Optional)()),
tslib_1.__param(0, (0, common_1.Inject)(files_constants_1.MULTER_MODULE_OPTIONS)),
tslib_1.__metadata("design:paramtypes", [Object])
], MixinInterceptor);
const Interceptor = (0, common_1.mixin)(MixinInterceptor);
return Interceptor;
}

View File

@@ -0,0 +1,5 @@
export * from './any-files.interceptor';
export * from './file-fields.interceptor';
export * from './file.interceptor';
export * from './files.interceptor';
export * from './no-files.interceptor';

View File

@@ -0,0 +1,8 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const tslib_1 = require("tslib");
tslib_1.__exportStar(require("./any-files.interceptor"), exports);
tslib_1.__exportStar(require("./file-fields.interceptor"), exports);
tslib_1.__exportStar(require("./file.interceptor"), exports);
tslib_1.__exportStar(require("./files.interceptor"), exports);
tslib_1.__exportStar(require("./no-files.interceptor"), exports);

View File

@@ -0,0 +1,8 @@
import { NestInterceptor, Type } from '@nestjs/common';
import { MulterOptions } from '../interfaces/multer-options.interface';
/**
*
* @param localOptions
* @publicApi
*/
export declare function NoFilesInterceptor(localOptions?: MulterOptions): Type<NestInterceptor>;

View File

@@ -0,0 +1,41 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.NoFilesInterceptor = NoFilesInterceptor;
const tslib_1 = require("tslib");
const common_1 = require("@nestjs/common");
const multer = require("multer");
const files_constants_1 = require("../files.constants");
const multer_utils_1 = require("../multer/multer.utils");
/**
*
* @param localOptions
* @publicApi
*/
function NoFilesInterceptor(localOptions) {
let MixinInterceptor = class MixinInterceptor {
constructor(options = {}) {
this.multer = multer({
...options,
...localOptions,
});
}
async intercept(context, next) {
const ctx = context.switchToHttp();
await new Promise((resolve, reject) => this.multer.none()(ctx.getRequest(), ctx.getResponse(), (err) => {
if (err) {
const error = (0, multer_utils_1.transformException)(err);
return reject(error);
}
resolve();
}));
return next.handle();
}
};
MixinInterceptor = tslib_1.__decorate([
tslib_1.__param(0, (0, common_1.Optional)()),
tslib_1.__param(0, (0, common_1.Inject)(files_constants_1.MULTER_MODULE_OPTIONS)),
tslib_1.__metadata("design:paramtypes", [Object])
], MixinInterceptor);
const Interceptor = (0, common_1.mixin)(MixinInterceptor);
return Interceptor;
}

View File

@@ -0,0 +1,18 @@
import { ModuleMetadata, Type } from '@nestjs/common/interfaces';
import { MulterOptions } from './multer-options.interface';
export type MulterModuleOptions = MulterOptions;
/**
* @publicApi
*/
export interface MulterOptionsFactory {
createMulterOptions(): Promise<MulterModuleOptions> | MulterModuleOptions;
}
/**
* @publicApi
*/
export interface MulterModuleAsyncOptions extends Pick<ModuleMetadata, 'imports'> {
useExisting?: Type<MulterOptionsFactory>;
useClass?: Type<MulterOptionsFactory>;
useFactory?: (...args: any[]) => Promise<MulterModuleOptions> | MulterModuleOptions;
inject?: any[];
}

View File

@@ -0,0 +1,2 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });

View File

@@ -0,0 +1 @@
export * from './files-upload-module.interface';

View File

@@ -0,0 +1,4 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const tslib_1 = require("tslib");
tslib_1.__exportStar(require("./files-upload-module.interface"), exports);

View File

@@ -0,0 +1,61 @@
/**
* @see https://github.com/expressjs/multer
*
* @publicApi
*/
export interface MulterOptions {
dest?: string | Function;
/** The storage engine to use for uploaded files. */
storage?: any;
/**
* An object specifying the size limits of the following optional properties. This object is passed to busboy
* directly, and the details of properties can be found on https://github.com/mscdex/busboy#busboy-methods
*/
limits?: {
/** Max field name size (Default: 100 bytes) */
fieldNameSize?: number;
/** Max field value size (Default: 1MB) */
fieldSize?: number;
/** Max number of non- file fields (Default: Infinity) */
fields?: number;
/** For multipart forms, the max file size (in bytes)(Default: Infinity) */
fileSize?: number;
/** For multipart forms, the max number of file fields (Default: Infinity) */
files?: number;
/** For multipart forms, the max number of parts (fields + files)(Default: Infinity) */
parts?: number;
/** For multipart forms, the max number of header key=> value pairs to parse Default: 2000(same as node's http). */
headerPairs?: number;
};
/** Keep the full path of files instead of just the base name (Default: false) */
preservePath?: boolean;
fileFilter?(req: any, file: {
/** Field name specified in the form */
fieldname: string;
/** Name of the file on the user's computer */
originalname: string;
/** Encoding type of the file */
encoding: string;
/** Mime type of the file */
mimetype: string;
/** Size of the file in bytes */
size: number;
/** The folder to which the file has been saved (DiskStorage) */
destination: string;
/** The name of the file within the destination (DiskStorage) */
filename: string;
/** Location of the uploaded file (DiskStorage) */
path: string;
/** A Buffer of the entire file (MemoryStorage) */
buffer: Buffer;
}, callback: (error: Error | null, acceptFile: boolean) => void): void;
}
/**
* @publicApi
*/
export interface MulterField {
/** The field name. */
name: string;
/** Optional maximum number of files per field to accept. */
maxCount?: number;
}

View File

@@ -0,0 +1,2 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });

View File

@@ -0,0 +1 @@
export declare const MULTER_MODULE_ID = "MULTER_MODULE_ID";

View File

@@ -0,0 +1,4 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.MULTER_MODULE_ID = void 0;
exports.MULTER_MODULE_ID = 'MULTER_MODULE_ID';

View File

@@ -0,0 +1,11 @@
import { DynamicModule } from '@nestjs/common';
import { MulterModuleAsyncOptions, MulterModuleOptions } from './interfaces/files-upload-module.interface';
/**
* @publicApi
*/
export declare class MulterModule {
static register(options?: MulterModuleOptions): DynamicModule;
static registerAsync(options: MulterModuleAsyncOptions): DynamicModule;
private static createAsyncProviders;
private static createAsyncOptionsProvider;
}

View File

@@ -0,0 +1,71 @@
"use strict";
var MulterModule_1;
Object.defineProperty(exports, "__esModule", { value: true });
exports.MulterModule = void 0;
const tslib_1 = require("tslib");
const common_1 = require("@nestjs/common");
const random_string_generator_util_1 = require("@nestjs/common/utils/random-string-generator.util");
const files_constants_1 = require("./files.constants");
const multer_constants_1 = require("./multer.constants");
/**
* @publicApi
*/
let MulterModule = MulterModule_1 = class MulterModule {
static register(options = {}) {
return {
module: MulterModule_1,
providers: [
{ provide: files_constants_1.MULTER_MODULE_OPTIONS, useFactory: () => options },
{
provide: multer_constants_1.MULTER_MODULE_ID,
useValue: (0, random_string_generator_util_1.randomStringGenerator)(),
},
],
exports: [files_constants_1.MULTER_MODULE_OPTIONS],
};
}
static registerAsync(options) {
return {
module: MulterModule_1,
imports: options.imports,
providers: [
...this.createAsyncProviders(options),
{
provide: multer_constants_1.MULTER_MODULE_ID,
useValue: (0, random_string_generator_util_1.randomStringGenerator)(),
},
],
exports: [files_constants_1.MULTER_MODULE_OPTIONS],
};
}
static createAsyncProviders(options) {
if (options.useExisting || options.useFactory) {
return [this.createAsyncOptionsProvider(options)];
}
return [
this.createAsyncOptionsProvider(options),
{
provide: options.useClass,
useClass: options.useClass,
},
];
}
static createAsyncOptionsProvider(options) {
if (options.useFactory) {
return {
provide: files_constants_1.MULTER_MODULE_OPTIONS,
useFactory: options.useFactory,
inject: options.inject || [],
};
}
return {
provide: files_constants_1.MULTER_MODULE_OPTIONS,
useFactory: async (optionsFactory) => optionsFactory.createMulterOptions(),
inject: [options.useExisting || options.useClass],
};
}
};
exports.MulterModule = MulterModule;
exports.MulterModule = MulterModule = MulterModule_1 = tslib_1.__decorate([
(0, common_1.Module)({})
], MulterModule);

View File

@@ -0,0 +1,16 @@
export declare const multerExceptions: {
LIMIT_PART_COUNT: string;
LIMIT_FILE_SIZE: string;
LIMIT_FILE_COUNT: string;
LIMIT_FIELD_KEY: string;
LIMIT_FIELD_VALUE: string;
LIMIT_FIELD_COUNT: string;
LIMIT_UNEXPECTED_FILE: string;
MISSING_FIELD_NAME: string;
};
export declare const busboyExceptions: {
MULTIPART_BOUNDARY_NOT_FOUND: string;
MULTIPART_MALFORMED_PART_HEADER: string;
MULTIPART_UNEXPECTED_END_OF_FORM: string;
MULTIPART_UNEXPECTED_END_OF_FILE: string;
};

View File

@@ -0,0 +1,21 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.busboyExceptions = exports.multerExceptions = void 0;
exports.multerExceptions = {
// from https://github.com/expressjs/multer/blob/master/lib/multer-error.js
LIMIT_PART_COUNT: 'Too many parts',
LIMIT_FILE_SIZE: 'File too large',
LIMIT_FILE_COUNT: 'Too many files',
LIMIT_FIELD_KEY: 'Field name too long',
LIMIT_FIELD_VALUE: 'Field value too long',
LIMIT_FIELD_COUNT: 'Too many fields',
LIMIT_UNEXPECTED_FILE: 'Unexpected field',
MISSING_FIELD_NAME: 'Field name missing',
};
exports.busboyExceptions = {
// from https://github.com/mscdex/busboy/blob/master/lib/types/multipart.js
MULTIPART_BOUNDARY_NOT_FOUND: 'Multipart: Boundary not found',
MULTIPART_MALFORMED_PART_HEADER: 'Malformed part header',
MULTIPART_UNEXPECTED_END_OF_FORM: 'Unexpected end of form',
MULTIPART_UNEXPECTED_END_OF_FILE: 'Unexpected end of file',
};

View File

@@ -0,0 +1 @@
export declare function transformException(error: Error | undefined): Error;

View File

@@ -0,0 +1,29 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.transformException = transformException;
const common_1 = require("@nestjs/common");
const multer_constants_1 = require("./multer.constants");
function transformException(error) {
if (!error || error instanceof common_1.HttpException) {
return error;
}
switch (error.message) {
case multer_constants_1.multerExceptions.LIMIT_FILE_SIZE:
return new common_1.PayloadTooLargeException(error.message);
case multer_constants_1.multerExceptions.LIMIT_FILE_COUNT:
case multer_constants_1.multerExceptions.LIMIT_FIELD_KEY:
case multer_constants_1.multerExceptions.LIMIT_FIELD_VALUE:
case multer_constants_1.multerExceptions.LIMIT_FIELD_COUNT:
case multer_constants_1.multerExceptions.LIMIT_UNEXPECTED_FILE:
case multer_constants_1.multerExceptions.LIMIT_PART_COUNT:
case multer_constants_1.multerExceptions.MISSING_FIELD_NAME:
return new common_1.BadRequestException(error.message);
case multer_constants_1.busboyExceptions.MULTIPART_BOUNDARY_NOT_FOUND:
return new common_1.BadRequestException(error.message);
case multer_constants_1.busboyExceptions.MULTIPART_MALFORMED_PART_HEADER:
case multer_constants_1.busboyExceptions.MULTIPART_UNEXPECTED_END_OF_FORM:
case multer_constants_1.busboyExceptions.MULTIPART_UNEXPECTED_END_OF_FILE:
return new common_1.BadRequestException(`Multipart: ${error.message}`);
}
return error;
}