260 lines
8.8 KiB
PHP
260 lines
8.8 KiB
PHP
<div class="container mt-5">
|
|
<h2 class="text-center">Add Campaign Data</h2>
|
|
<?php if (isset($error) && $error) : ?>
|
|
<div class="alert alert-danger" role="alert">
|
|
Please fill in all required fields!
|
|
</div>
|
|
<?php endif; ?>
|
|
|
|
<form action="/<?php echo $_SESSION['role']; ?>/campaign/add" method="POST" class="mt-4">
|
|
<div class="form-group">
|
|
<label for="name">Name</label>
|
|
<input type="name" name="name" id="name" class="form-control" required>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="file_id">Google Sheet</label>
|
|
<div class="card">
|
|
<div class="card-body text-center p-4">
|
|
<div class="mb-3">
|
|
<input type="text" name="file_id" id="file_id" class="form-control bg-white text-center"
|
|
placeholder="No file selected" readonly required>
|
|
</div>
|
|
<button type="button" class="btn btn-primary" onclick="openDrivePicker()">
|
|
<i class="fas fa-file-excel mr-2"></i>Select Google Sheet
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<button type="submit" class="btn btn-primary">Submit</button>
|
|
</form>
|
|
</div>
|
|
|
|
<style>
|
|
.drive-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
|
gap: 1rem;
|
|
padding: 1rem;
|
|
}
|
|
|
|
.drive-item {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
padding: 1rem;
|
|
border: 1px solid #dee2e6;
|
|
border-radius: 0.25rem;
|
|
cursor: pointer;
|
|
transition: background-color 0.2s;
|
|
text-align: center;
|
|
}
|
|
|
|
.drive-item:hover {
|
|
background-color: #f8f9fa;
|
|
}
|
|
|
|
.drive-item svg {
|
|
width: 48px;
|
|
height: 48px;
|
|
margin-bottom: 0.5rem;
|
|
}
|
|
|
|
.drive-item-name {
|
|
font-size: 0.9rem;
|
|
word-break: break-word;
|
|
max-width: 100%;
|
|
}
|
|
|
|
.drive-breadcrumb {
|
|
padding: 0.5rem 1rem;
|
|
background-color: #f8f9fa;
|
|
border-bottom: 1px solid #dee2e6;
|
|
}
|
|
|
|
.drive-breadcrumb a {
|
|
color: #007bff;
|
|
text-decoration: none;
|
|
}
|
|
|
|
.drive-breadcrumb a:hover {
|
|
text-decoration: underline;
|
|
}
|
|
|
|
.modal-body-scroll {
|
|
max-height: 60vh;
|
|
overflow-y: auto;
|
|
}
|
|
|
|
.drive-loader {
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
min-height: 200px;
|
|
}
|
|
|
|
.drive-loader-spinner {
|
|
width: 50px;
|
|
height: 50px;
|
|
border: 5px solid #f3f3f3;
|
|
border-top: 5px solid #3498db;
|
|
border-radius: 50%;
|
|
animation: spin 1s linear infinite;
|
|
}
|
|
|
|
@keyframes spin {
|
|
0% { transform: rotate(0deg); }
|
|
100% { transform: rotate(360deg); }
|
|
}
|
|
|
|
.drive-grid {
|
|
min-height: 300px; /* Prevent layout shift */
|
|
}
|
|
|
|
.drive-content {
|
|
transition: opacity 0.2s;
|
|
}
|
|
|
|
.drive-content.loading {
|
|
opacity: 0.5;
|
|
pointer-events: none;
|
|
}
|
|
</style>
|
|
|
|
<script>
|
|
let currentFolderId = 'root';
|
|
let folderPath = [{id: 'root', name: 'My Drive'}];
|
|
let currentModal = null;
|
|
|
|
async function createModal() {
|
|
const modal = document.createElement('div');
|
|
modal.className = 'modal fade';
|
|
modal.innerHTML = `
|
|
<div class="modal-dialog modal-xl">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title">Select Google Sheet</h5>
|
|
<button type="button" class="close" data-dismiss="modal">
|
|
<span>×</span>
|
|
</button>
|
|
</div>
|
|
<div class="drive-breadcrumb">
|
|
${renderBreadcrumb()}
|
|
</div>
|
|
<div class="modal-body p-0 modal-body-scroll">
|
|
<div class="drive-content">
|
|
<div class="drive-loader">
|
|
<div class="drive-loader-spinner"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
`;
|
|
|
|
document.body.appendChild(modal);
|
|
currentModal = modal;
|
|
|
|
// Handle modal cleanup on close
|
|
$(modal).on('hidden.bs.modal', function () {
|
|
modal.remove();
|
|
currentModal = null;
|
|
});
|
|
|
|
$(modal).modal('show');
|
|
return modal;
|
|
}
|
|
|
|
async function updateModalContent(modal, files) {
|
|
const content = modal.querySelector('.drive-content');
|
|
content.innerHTML = `
|
|
<div class="drive-grid">
|
|
${files.length === 0 ?
|
|
'<div class="text-center p-4 w-100">No files found in this folder</div>' :
|
|
files.map(file => `
|
|
<div class="drive-item ${file.mimeType === 'application/vnd.google-apps.folder' ? 'folder' : 'sheet'}"
|
|
onclick="${file.mimeType === 'application/vnd.google-apps.folder' ?
|
|
`openFolder('${file.id}', '${file.name}')` :
|
|
`selectFile('${file.id}', '${file.name}')`}">
|
|
${file.mimeType === 'application/vnd.google-apps.folder' ?
|
|
`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 50">
|
|
<path d="M 5 4 C 3.3544268 4 2 5.3555411 2 7 L 2 16 L 2 26 L 2 43 C 2 44.644459 3.3544268 46 5 46 L 45 46 C 46.645063 46 48 44.645063 48 43 L 48 26 L 48 16 L 48 11 C 48 9.3549372 46.645063 8 45 8 L 18 8 C 18.08657 8 17.96899 8.000364 17.724609 7.71875 C 17.480227 7.437136 17.179419 6.9699412 16.865234 6.46875 C 16.55105 5.9675588 16.221777 5.4327899 15.806641 4.9628906 C 15.391504 4.4929914 14.818754 4 14 4 L 5 4 z M 5 6 L 14 6 C 13.93925 6 14.06114 6.00701 14.308594 6.2871094 C 14.556051 6.5672101 14.857231 7.0324412 15.169922 7.53125 C 15.482613 8.0300588 15.806429 8.562864 16.212891 9.03125 C 16.619352 9.499636 17.178927 10 18 10 L 45 10 C 45.562937 10 46 10.437063 46 11 L 46 13.1875 C 45.685108 13.07394 45.351843 13 45 13 L 5 13 C 4.6481575 13 4.3148915 13.07394 4 13.1875 L 4 7 C 4 6.4364589 4.4355732 6 5 6 z M 5 15 L 45 15 C 45.56503 15 46 15.43497 46 16 L 46 26 L 46 43 C 46 43.562937 45.562937 44 45 44 L 5 44 C 4.4355732 44 4 43.563541 4 43 L 4 26 L 4 16 C 4 15.43497 4.4349698 15 5 15 z"></path>
|
|
</svg>` :
|
|
`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48">
|
|
<path fill="#43a047" d="M37,45H11c-1.657,0-3-1.343-3-3V6c0-1.657,1.343-3,3-3h19l10,10v29C40,43.657,38.657,45,37,45z"></path>
|
|
<path fill="#c8e6c9" d="M40 13L30 13 30 3z"></path>
|
|
<path fill="#2e7d32" d="M30 13L40 23 40 13z"></path>
|
|
<path fill="#e8f5e9" d="M31,23H17h-2v2v2v2v2v2v2v2h18v-2v-2v-2v-2v-2v-2v-2H31z M17,25h4v2h-4V25z M17,29h4v2h-4V29z M17,33h4v2h-4V33z M31,35h-8v-2h8V35z M31,31h-8v-2h8V31z M31,27h-8v-2h8V27z"></path>
|
|
</svg>`
|
|
}
|
|
<div class="drive-item-name">${file.name}</div>
|
|
</div>
|
|
`).join('')
|
|
}
|
|
</div>
|
|
`;
|
|
|
|
// Update breadcrumb
|
|
modal.querySelector('.drive-breadcrumb').innerHTML = renderBreadcrumb();
|
|
}
|
|
|
|
async function loadFiles(modal) {
|
|
const content = modal.querySelector('.drive-content');
|
|
content.classList.add('loading');
|
|
|
|
try {
|
|
const response = await fetch(`/drive/files?folderId=${currentFolderId}`);
|
|
if (!response.ok) {
|
|
throw new Error('Failed to fetch files');
|
|
}
|
|
|
|
const data = await response.json();
|
|
|
|
if (!data.files || !Array.isArray(data.files)) {
|
|
throw new Error('Invalid response format');
|
|
}
|
|
|
|
await updateModalContent(modal, data.files);
|
|
} catch (error) {
|
|
alert('Error loading files: ' + error.message);
|
|
// Reset to root if there's an error
|
|
currentFolderId = 'root';
|
|
folderPath = [{id: 'root', name: 'My Drive'}];
|
|
} finally {
|
|
content.classList.remove('loading');
|
|
}
|
|
}
|
|
|
|
async function openDrivePicker() {
|
|
if (!currentModal) {
|
|
const modal = await createModal();
|
|
await loadFiles(modal);
|
|
}
|
|
}
|
|
|
|
async function openFolder(folderId, folderName) {
|
|
currentFolderId = folderId;
|
|
folderPath.push({id: folderId, name: folderName});
|
|
await loadFiles(currentModal);
|
|
}
|
|
|
|
async function navigateToFolder(index) {
|
|
currentFolderId = folderPath[index].id;
|
|
folderPath = folderPath.slice(0, index + 1);
|
|
await loadFiles(currentModal);
|
|
}
|
|
|
|
function selectFile(fileId, fileName) {
|
|
document.getElementById('file_id').value = fileId;
|
|
if (currentModal) {
|
|
$(currentModal).modal('hide');
|
|
}
|
|
}
|
|
|
|
function renderBreadcrumb() {
|
|
return folderPath.map((folder, index) => `
|
|
<a href="#" onclick="event.preventDefault(); navigateToFolder(${index})">${folder.name}</a>
|
|
${index < folderPath.length - 1 ? ' / ' : ''}
|
|
`).join('');
|
|
}
|
|
</script>
|