emmanuelbarrameda

take-picture.blade.php

Apr 14th, 2025
56
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
PHP 26.92 KB | None | 0 0
  1. @php
  2.     $isDisabled = $field->isDisabled();
  3. @endphp
  4.  
  5. <x-dynamic-component
  6.     :component="$getFieldWrapperView()"
  7.     :field="$field"
  8. >
  9.     <div
  10.         x-data="{
  11.            photoData: $wire.entangle('{{ $getStatePath() }}'),
  12.            photoSelected: false,
  13.            webcamActive: false,
  14.            webcamError: null,
  15.            cameraStream: null,
  16.            availableCameras: [],
  17.            selectedCameraId: null,
  18.            modalOpen: false,
  19.            aspectRatio: '{{ $getAspect() }}',
  20.            imageQuality: {{ $getImageQuality() }},
  21.            mirroredView: true,
  22.            isDisabled: {{ json_encode($isDisabled) }},
  23.            urlPrefix: '{{ $getImageUrlPrefix() }}',
  24.            isMobile: /iPhone|iPad|iPod|Android/i.test(navigator.userAgent),
  25.            currentFacingMode: 'environment',
  26.            
  27.            getImageUrl(path) {
  28.                if (!path) return null;
  29.                if (path.startsWith('data:image/')) return path;
  30.                // Only prepend the URL prefix if it's a path and not already a full URL
  31.                if (!path.startsWith('http://') && !path.startsWith('https://')) {
  32.                    return this.urlPrefix + path;
  33.                }
  34.                return path;
  35.            },
  36.  
  37.            async getCameras() {
  38.                try {
  39.                    const devices = await navigator.mediaDevices.enumerateDevices();
  40.                    this.availableCameras = devices.filter(device => device.kind === 'videoinput');
  41.                    
  42.                    if (this.availableCameras.length > 0 && !this.selectedCameraId) {
  43.                        // Default to the first camera (usually front-facing on mobile)
  44.                        this.selectedCameraId = this.availableCameras[0].deviceId;
  45.                    }
  46.                    
  47.                    return this.availableCameras;
  48.                } catch (error) {
  49.                    console.error('Error getting camera devices:', error);
  50.                    this.webcamError = '{{ __('Unable to detect available cameras') }}';
  51.                    return [];
  52.                }
  53.            },
  54.            
  55.            async initWebcam() {
  56.                if (this.isDisabled) return;
  57.                this.webcamActive = true;
  58.                this.webcamError = null;
  59.                
  60.                if ({{ $getShowCameraSelector() ? 'true' : 'false' }} && this.availableCameras.length === 0) {
  61.                    await this.getCameras();
  62.                }
  63.                
  64.                // Calculate aspect ratio for constraints
  65.                let aspectWidth = 16;
  66.                let aspectHeight = 9;
  67.                
  68.                if (this.aspectRatio) {
  69.                    const parts = this.aspectRatio.split(':');
  70.                    if (parts.length === 2) {
  71.                        aspectWidth = parseInt(parts[0]);
  72.                        aspectHeight = parseInt(parts[1]);
  73.                    }
  74.                }
  75.                
  76.                const constraints = {
  77.                    video: {
  78.                        facingMode: 'user',
  79.                        width: { ideal: aspectWidth * 120 },
  80.                        height: { ideal: aspectHeight * 120 }
  81.                    }
  82.                };
  83.                audio: false
  84.                
  85.                // If a specific camera is selected, use its deviceId
  86.                if (this.selectedCameraId) {
  87.                    constraints.video.deviceId = { exact: this.selectedCameraId };
  88.                }
  89.                
  90.                try {
  91.                    const stream = await navigator.mediaDevices.getUserMedia(constraints);
  92.                    this.cameraStream = stream;
  93.                    this.$refs.video.srcObject = stream;
  94.                    
  95.                    // Auto-open modal if configured
  96.                    if ({{ $getUseModal() ? 'true' : 'false' }} && !this.modalOpen) {
  97.                        this.openModal();
  98.                    }
  99.                } catch (error) {
  100.                    console.error('Error accessing webcam:', error);
  101.                    this.handleWebcamError(error);
  102.                }
  103.  
  104.                if (this.isMobile) {
  105.                    constraints.video.facingMode = 'environment';
  106.                }
  107.  
  108.                if (this.isMobile) {
  109.                    constraints.video.facingMode = this.currentFacingMode;
  110.                }
  111.  
  112.            },
  113.            
  114.            handleWebcamError(error) {
  115.  
  116.                // Mobile-specific errors
  117.                if (/iPhone|iPad|iPod|Android/i.test(navigator.userAgent)) {
  118.                    if (window.location.protocol !== 'https:') {
  119.                        this.webcamError = '{{ __('Camera access requires HTTPS on mobile devices') }}';
  120.                        return;
  121.                    }
  122.                }
  123.  
  124.                switch (error.name) {
  125.                    case 'NotAllowedError':
  126.                    case 'PermissionDeniedError':
  127.                        this.webcamError = '{{ __('Permission denied. Please allow camera access') }}';
  128.                        break;
  129.                    case 'NotFoundError':
  130.                    case 'DevicesNotFoundError':
  131.                        this.webcamError = '{{ __('No available or connected camera found') }}';
  132.                        break;
  133.                    case 'NotReadableError':
  134.                    case 'TrackStartError':
  135.                        this.webcamError = '{{ __('The camera is in use by another application or cannot be accessed') }}';
  136.                        break;
  137.                    case 'OverconstrainedError':
  138.                        this.webcamError = '{{ __('Could not meet the requested camera constraints') }}';
  139.                        break;
  140.                    case 'SecurityError':
  141.                        this.webcamError = '{{ __('Access blocked for security reasons. Use HTTPS or a trusted browser') }}';
  142.                        break;
  143.                    case 'AbortError':
  144.                        this.webcamError = '{{ __('The camera access operation was canceled') }}';
  145.                        break;
  146.                    default:
  147.                        this.webcamError = '{{ __('An unknown error occurred while trying to open the camera') }}';
  148.                }
  149.            },
  150.            
  151.            async changeCamera(cameraId) {
  152.                this.selectedCameraId = cameraId;
  153.                if (this.webcamActive) {
  154.                    this.stopCamera();
  155.                    await this.$nextTick();
  156.                    this.initWebcam();
  157.                }
  158.            },
  159.            
  160.            capturePhoto() {
  161.                const video = this.$refs.video;
  162.                const canvas = document.createElement('canvas');
  163.                
  164.                // Use actual video dimensions for better quality
  165.                canvas.width = video.videoWidth;
  166.                canvas.height = video.videoHeight;
  167.                
  168.                const context = canvas.getContext('2d');
  169.                
  170.                // Handle mirroring correctly during capture
  171.                if (this.mirroredView) {
  172.                    // If mirrored view, we need to flip the capture back to normal
  173.                    context.translate(canvas.width, 0);
  174.                    context.scale(-1, 1);
  175.                }
  176.                
  177.                context.drawImage(video, 0, 0, canvas.width, canvas.height);
  178.                
  179.                // Apply quality setting (0-1)
  180.                const quality = this.imageQuality / 100;
  181.                this.photoData = canvas.toDataURL('image/jpeg', quality);
  182.                
  183.                // Automatically shut down camera after capturing
  184.                this.stopCamera();
  185.                
  186.                // Close modal if it was open
  187.                if (this.modalOpen) {
  188.                    this.closeModal();
  189.                }
  190.            },
  191.            
  192.            usePhoto() {
  193.                this.photoSelected = true;
  194.                // Already set in photoData via entangle
  195.            },
  196.            
  197.            retakePhoto() {
  198.                this.photoSelected = false;
  199.                this.initWebcam();
  200.            },
  201.            
  202.            stopCamera() {
  203.                this.webcamActive = false;
  204.                if (this.cameraStream) {
  205.                    this.cameraStream.getTracks().forEach(track => track.stop());
  206.                    this.cameraStream = null;
  207.                }
  208.            },
  209.            
  210.            toggleCamera() {
  211.                if (this.webcamActive) {
  212.                    this.stopCamera();
  213.                } else {
  214.                    this.initWebcam();
  215.                }
  216.            },
  217.            
  218.            toggleMirror() {
  219.                this.mirroredView = !this.mirroredView;
  220.            },
  221.  
  222.            flipCamera() {
  223.                this.currentFacingMode = this.currentFacingMode === 'environment' ? 'user' : 'environment';
  224.                if (this.webcamActive) {
  225.                    this.stopCamera();
  226.                    
  227.                    // Need to wait a moment before restarting
  228.                    setTimeout(() => {
  229.                        this.initWebcam();
  230.                    }, 300);
  231.                }
  232.            },
  233.            
  234.            isBase64Image() {
  235.                return this.photoData && this.photoData.startsWith('data:image/');
  236.            },
  237.            
  238.            clearPhoto() {
  239.                this.photoData = null;
  240.                this.photoSelected = false;
  241.            },
  242.            
  243.            openModal() {
  244.                this.modalOpen = true;
  245.                document.body.classList.add('overflow-hidden');
  246.            },
  247.            
  248.            closeModal() {
  249.                this.modalOpen = false;
  250.                document.body.classList.remove('overflow-hidden');
  251.                this.stopCamera();
  252.            }
  253.        }"
  254.         x-init="() => {
  255.  
  256.            if (window.location.protocol !== 'https:' && /iPhone|iPad|iPod|Android/i.test(navigator.userAgent)) {
  257.                webcamError = '{{ __('Camera access requires HTTPS on mobile devices') }}';
  258.            }
  259.  
  260.            if (!photoData) {
  261.                if ({{ $getUseModal() ? 'false' : 'true' }}) {
  262.                    initWebcam();
  263.                }
  264.            } else if (!isBase64Image()) {
  265.                photoSelected = true;
  266.            }
  267.            
  268.            if ({{ $getShowCameraSelector() ? 'true' : 'false' }}) {
  269.                getCameras();
  270.            }
  271.        }"
  272.         @keydown.escape.window="if (modalOpen) { closeModal(); } else { stopCamera(); }"
  273.         class="flex flex-col space-y-4"
  274.     >
  275.         <!-- preview thumbnail -->
  276.         <div class="flex items-center space-x-4">
  277.            
  278.             <div class="relative w-20 h-20">
  279.  
  280.                 <!-- photo-preview available -->
  281.                 <template x-if="photoData">
  282.                     <div class="relative w-20 h-20 rounded-lg overflow-hidden bg-gray-100 shadow-sm border border-gray-300"
  283.                         :class="{'cursor-default': {{ json_encode($isDisabled) }}, 'cursor-pointer hover:shadow-md': !{{ json_encode($isDisabled) }}}"
  284.                     >
  285.                         <!-- image gettting url -->
  286.                         <img :src="photoData ? getImageUrl(photoData) : ''" class="w-full h-full object-cover">
  287.                        
  288.                         <!-- Edit Button (visible only when not disabled) -->
  289.                         <div class="absolute bottom-0 right-0 p-1 bg-gray-800 bg-opacity-70 rounded-tl"
  290.                             x-show="!{{ json_encode($isDisabled) }}"
  291.                             @click.stop="initWebcam()">
  292.                             <svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 text-white" viewBox="0 0 20 20" fill="currentColor">
  293.                                 <path d="M13.586 3.586a2 2 0 112.828 2.828l-.793.793-2.828-2.828.793-.793zM11.379 5.793L3 14.172V17h2.828l8.38-8.379-2.83-2.828z" />
  294.                             </svg>
  295.                         </div>
  296.                        
  297.                         <!-- Preview button (visible in both enabled and disabled states) -->
  298.                         <div class="absolute top-0 left-0 p-1 bg-gray-800 bg-opacity-70 rounded-br">
  299.                             <a :href="getImageUrl(photoData)" target="_blank" @click.stop class="block">
  300.                                 <svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 text-white" viewBox="0 0 20 20" fill="currentColor">
  301.                                     <path d="M10 12a2 2 0 100-4 2 2 0 000 4z" />
  302.                                     <path fill-rule="evenodd" d="M.458 10C1.732 5.943 5.522 3 10 3s8.268 2.943 9.542 7c-1.274 4.057-5.064 7-9.542 7S1.732 14.057.458 10zM14 10a4 4 0 11-8 0 4 4 0 018 0z" clip-rule="evenodd" />
  303.                                 </svg>
  304.                             </a>
  305.                         </div>
  306.                     </div>
  307.                 </template>
  308.                
  309.                 <!-- take photo button -->
  310.                 <template x-if="!photoData">
  311.                     <button
  312.                         type="button"
  313.                         @click="!{{ json_encode($isDisabled) }} && initWebcam()"
  314.                         class="w-24 h-24 rounded-lg border border-dashed border-gray-400 flex flex-col items-center justify-center bg-gray-50 hover:bg-gray-100 cursor-pointer transition-colors"
  315.                         :class="{'cursor-default pointer-events-none opacity-70': {{ json_encode($isDisabled) }}, 'cursor-pointer': !{{ json_encode($isDisabled) }}}"
  316.                     >
  317.                         <svg xmlns="http://www.w3.org/2000/svg" class="h-8 w-8 text-gray-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
  318.                             <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 9a2 2 0 012-2h.93a2 2 0 001.664-.89l.812-1.22A2 2 0 0110.07 4h3.86a2 2 0 011.664.89l.812 1.22A2 2 0 0018.07 7H19a2 2 0 012 2v9a2 2 0 01-2 2H5a2 2 0 01-2-2V9z" />
  319.                             <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 13a3 3 0 11-6 0 3 3 0 016 0z" />
  320.                         </svg>
  321.                         <span class="mt-1 text-xs text-gray-600">{{ __('Take Photo') }}</span>
  322.                     </button>
  323.                 </template>
  324.                
  325.                 <!-- clear button -->
  326.                 <template x-if="photoData && !{{ json_encode($isDisabled) }}">
  327.                     <button
  328.                         type="button"
  329.                         @click.stop="clearPhoto()"
  330.                         class="absolute -top-2 -right-2 p-1 bg-red-500 text-white rounded-full shadow-sm hover:bg-red-600 transition-colors"
  331.                     >
  332.                         <svg xmlns="http://www.w3.org/2000/svg" class="h-3 w-3" viewBox="0 0 20 20" fill="currentColor">
  333.                             <path fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd" />
  334.                         </svg>
  335.                     </button>
  336.                 </template>
  337.                
  338.             </div>
  339.            
  340.             <!-- help text -->
  341.             @if (!$isDisabled)
  342.                 <div class="text-sm ml-4">
  343.                     <p class="text-gray-700 font-medium mb-1">{{ $getLabel() }}</p>
  344.                     <p class="text-gray-500 text-xs">{{ __('Click to capture a new photo') }}</p>
  345.                 </div>
  346.             @endif
  347.         </div>
  348.        
  349.         <!-- display error message, when accessing the camera -->
  350.         <template x-if="webcamError && !modalOpen">
  351.             <div class="text-red-500 bg-red-50 py-2 px-3 rounded text-sm">
  352.                 <span x-text="webcamError"></span>
  353.             </div>
  354.         </template>
  355.  
  356.         <!-- field to store the captured picture -->
  357.         <input type="hidden" {{ $applyStateBindingModifiers('wire:model') }}="{{ $getStatePath() }}">
  358.        
  359.         <!-- MODAL -->
  360.         <template x-teleport="body">
  361.             <div
  362.                 x-show="modalOpen"
  363.                 @click.self="closeModal()"
  364.                 x-transition:enter="transition ease-out duration-200"
  365.                 x-transition:enter-start="opacity-0"
  366.                 x-transition:enter-end="opacity-100"
  367.                 x-transition:leave="transition ease-in duration-150"
  368.                 x-transition:leave-start="opacity-100"
  369.                 x-transition:leave-end="opacity-0"
  370.                 class="fixed inset-0 z-50 bg-black bg-opacity-75 flex items-center justify-center p-4"
  371.                 style="display: none;"
  372.             >
  373.                 <div
  374.                     @click.stop
  375.                     x-transition:enter="transition ease-out duration-200"
  376.                     x-transition:enter-start="opacity-0 scale-95"
  377.                     x-transition:enter-end="opacity-100 scale-100"
  378.                     x-transition:leave="transition ease-in duration-150"
  379.                     x-transition:leave-start="opacity-100 scale-100"
  380.                     x-transition:leave-end="opacity-0 scale-95"
  381.                     class="bg-white dark:bg-gray-800 rounded-lg shadow-xl w-full max-w-lg overflow-hidden"
  382.                 >
  383.                     <!-- MODAL HEADER -->
  384.                     <div class="px-4 py-3 border-b border-gray-200 dark:border-gray-700 flex justify-between items-center">
  385.                         <h3 class="text-lg font-medium text-gray-900 dark:text-gray-100">
  386.                             {{ __('Take Photo') }}
  387.                         </h3>
  388.                         <button
  389.                             type="button"
  390.                             @click="closeModal()"
  391.                             class="text-gray-400 hover:text-gray-500"
  392.                         >
  393.                             <svg class="h-5 w-5" fill="currentColor" viewBox="0 0 20 20">
  394.                                 <path fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd" />
  395.                             </svg>
  396.                         </button>
  397.                     </div>
  398.                    
  399.                     <!-- MODAL BODY -->
  400.                     <div class="p-4">
  401.                         <!-- CAMERA VIEW  -->
  402.                         <div class="relative bg-black rounded-lg overflow-hidden mb-4">
  403.                             <!-- PRVIEW -->
  404.                             <template x-if="webcamActive && !webcamError">
  405.                                 <div class="aspect-video flex items-center justify-center">
  406.                                     <video
  407.                                         x-ref="video"
  408.                                         autoplay
  409.                                         playsinline
  410.                                         :style="mirroredView ? 'transform: scaleX(-1);' : ''"
  411.                                         class="max-w-full max-h-[60vh] object-contain"
  412.                                     ></video>
  413.                                 </div>
  414.                             </template>
  415.                            
  416.                             <!-- ERROR -->
  417.                             <template x-if="webcamError">
  418.                                 <div class="aspect-video bg-gray-800 flex flex-col items-center justify-center text-center p-6">
  419.                                     <svg xmlns="http://www.w3.org/2000/svg" class="h-12 w-12 text-red-500 mb-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
  420.                                         <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
  421.                                     </svg>
  422.                                     <span class="text-white text-lg font-medium" x-text="webcamError"></span>
  423.  
  424.                                     <button
  425.                                         type="button"
  426.                                         @click="webcamError = null; initWebcam()"
  427.                                         class="mt-4 px-4 py-2 bg-blue-600 text-white text-sm font-medium rounded-md hover:bg-blue-700"
  428.                                     >
  429.                                         {{ __('Try Again') }}
  430.                                     </button>
  431.                                    
  432.                                 </div>
  433.                             </template>
  434.                            
  435.                             <!-- TAKE PHOTO BUTTON -->
  436.                             <template x-if="webcamActive && !webcamError">
  437.                                 <div class="absolute bottom-4 left-0 right-0 flex justify-center">
  438.                                     <button
  439.                                         type="button"
  440.                                         @click="capturePhoto()"
  441.                                         class="w-16 h-16 rounded-full bg-primary-600 hover:bg-primary-700 border-4 border-white flex items-center justify-center shadow-lg"
  442.                                         title="{{ __('Take Photo') }}"
  443.                                     >
  444.                                         <svg xmlns="http://www.w3.org/2000/svg" class="h-8 w-8 text-white" fill="none" viewBox="0 0 24 24" stroke="currentColor">
  445.                                             <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 9a2 2 0 012-2h.93a2 2 0 001.664-.89l.812-1.22A2 2 0 0110.07 4h3.86a2 2 0 011.664.89l.812 1.22A2 2 0 0018.07 7H19a2 2 0 012 2v9a2 2 0 01-2 2H5a2 2 0 01-2-2V9z" />
  446.                                         </svg>
  447.                                     </button>
  448.                                 </div>
  449.                             </template>
  450.                            
  451.                             <!-- MIRROR -->
  452.                             <template x-if="webcamActive && !webcamError">
  453.                                 <div class="absolute top-4 right-4">
  454.                                     <button
  455.                                         type="button"
  456.                                         @click="toggleMirror()"
  457.                                         class="w-10 h-10 rounded-full bg-black bg-opacity-50 text-white flex items-center justify-center"
  458.                                         :title="mirroredView ? disableMirrorText : enableMirrorText"
  459.                                     >
  460.                                         <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
  461.                                             <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7h12m0 0l-4-4m4 4l-4 4m0 6H4m0 0l4 4m-4-4l4-4" />
  462.                                         </svg>
  463.                                     </button>
  464.                                 </div>
  465.                             </template>
  466.  
  467.                             <!-- FLIP on MOBILE ONLY -->
  468.                             <template x-if="webcamActive && !webcamError && isMobile">
  469.                                 <div class="absolute top-4 left-4">
  470.                                     <button
  471.                                         type="button"
  472.                                         @click="flipCamera()"
  473.                                         class="w-10 h-10 rounded-full bg-black bg-opacity-50 text-white flex items-center justify-center"
  474.                                         title="{{ __('Flip Camera') }}"
  475.                                     >
  476.                                         <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
  477.                                             <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 4h13M3 8h9m-9 4h6m4 0l4-4m0 0l4 4m-4-4v12" />
  478.                                         </svg>
  479.                                     </button>
  480.                                 </div>
  481.                             </template>
  482.  
  483.  
  484.                         </div>
  485.                        
  486.                         <!-- CAMERA SELECTOR DROPDOWN -->
  487.                         <template x-if="{{ $getShowCameraSelector() ? 'true' : 'false' }} && availableCameras.length > 1">
  488.                             <div class="mb-4">
  489.                                 <label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
  490.                                     {{ __('Select Camera') }}
  491.                                 </label>
  492.                                 <select
  493.                                     x-model="selectedCameraId"
  494.                                     @change="changeCamera($event.target.value)"
  495.                                     class="block w-full border-gray-300 dark:border-gray-700 dark:bg-gray-900 dark:text-gray-300 focus:border-primary-500 focus:ring-primary-500 rounded-md shadow-sm"
  496.                                 >
  497.                                     <template x-for="(camera, index) in availableCameras" :key="camera.deviceId">
  498.                                         <option :value="camera.deviceId" x-text="`Camera ${index + 1} (${camera.label || 'Unnamed Camera'})`"></option>
  499.                                     </template>
  500.                                 </select>
  501.                             </div>
  502.                         </template>
  503.                        
  504.                         <!-- ACTION BUTTONS -->
  505.                         <div class="flex justify-end space-x-3">
  506.                             <button
  507.                                 type="button"
  508.                                 @click="closeModal()"
  509.                                 class="px-4 py-2 text-sm font-medium text-gray-700 dark:text-gray-300 bg-white dark:bg-gray-700 border border-gray-300 dark:border-gray-600 rounded-md shadow-sm hover:bg-gray-50 dark:hover:bg-gray-600"
  510.                             >
  511.                                 {{ __('Cancel') }}
  512.                             </button>
  513.                            
  514.                             <!-- TAKE PHOTO -->
  515.                             <button
  516.                                 type="button"
  517.                                 @click="capturePhoto()"
  518.                                 class="px-4 py-2 text-sm font-medium text-white bg-primary-600 border border-transparent rounded-md shadow-sm hover:bg-primary-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary-500"
  519.                             >
  520.                                 {{ __('Take Photo') }}
  521.                             </button>
  522.                         </div>
  523.                     </div>
  524.                 </div>
  525.             </div>
  526.         </template>
  527.     </div>
  528. </x-dynamic-component>
Add Comment
Please, Sign In to add comment