|
|
@@ -73,17 +73,66 @@ export class SitesPage {
|
|
|
<label for="site-url" class="block text-sm font-medium text-gray-700 dark:text-gray-300">
|
|
|
Site URL
|
|
|
</label>
|
|
|
+ <div class="relative">
|
|
|
+ <input
|
|
|
+ type="url"
|
|
|
+ id="site-url"
|
|
|
+ name="siteUrl"
|
|
|
+ required
|
|
|
+ class="input mt-1"
|
|
|
+ placeholder="https://example.com"
|
|
|
+ >
|
|
|
+ <div class="absolute right-3 top-1/2 -translate-y-1/2 mt-0.5 hidden" id="detect-spinner">
|
|
|
+ <div class="w-4 h-4 spinner"></div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <p class="mt-1 text-sm text-gray-500 dark:text-gray-400">
|
|
|
+ Enter the main URL of the site you want to scrape
|
|
|
+ </p>
|
|
|
+ <p class="mt-1 text-sm text-primary-600 dark:text-primary-400 hidden" id="detect-result"></p>
|
|
|
+ </div>
|
|
|
+ <div>
|
|
|
+ <label for="site-type-select" class="block text-sm font-medium text-gray-700 dark:text-gray-300">
|
|
|
+ Site Type
|
|
|
+ </label>
|
|
|
+ <select id="site-type-select" name="siteType" class="input mt-1">
|
|
|
+ <option value="webshop">Webshop</option>
|
|
|
+ <option value="website">Website</option>
|
|
|
+ </select>
|
|
|
+ </div>
|
|
|
+ <div>
|
|
|
+ <label for="site-platform-select" class="block text-sm font-medium text-gray-700 dark:text-gray-300">
|
|
|
+ Platform
|
|
|
+ </label>
|
|
|
+ <select id="site-platform-select" name="sitePlatform" class="input mt-1">
|
|
|
+ <option value="generic">Generic</option>
|
|
|
+ <option value="shoprenter">ShopRenter</option>
|
|
|
+ <option value="woocommerce">WooCommerce</option>
|
|
|
+ <option value="shopify">Shopify</option>
|
|
|
+ </select>
|
|
|
+ </div>
|
|
|
+ <div>
|
|
|
+ <label for="sitemap-url" class="block text-sm font-medium text-gray-700 dark:text-gray-300">
|
|
|
+ Sitemap URL
|
|
|
+ </label>
|
|
|
<input
|
|
|
type="url"
|
|
|
- id="site-url"
|
|
|
- name="siteUrl"
|
|
|
- required
|
|
|
+ id="sitemap-url"
|
|
|
+ name="sitemapUrl"
|
|
|
class="input mt-1"
|
|
|
- placeholder="https://example.com"
|
|
|
+ placeholder="https://example.com/sitemap.xml"
|
|
|
>
|
|
|
- <p class="mt-1 text-sm text-gray-500 dark:text-gray-400">
|
|
|
- Enter the main URL of the site you want to scrape
|
|
|
- </p>
|
|
|
+ <div class="flex items-center mt-2">
|
|
|
+ <input
|
|
|
+ type="checkbox"
|
|
|
+ id="no-sitemap"
|
|
|
+ name="noSitemap"
|
|
|
+ class="h-4 w-4 rounded border-gray-300 text-primary-600 focus:ring-primary-500"
|
|
|
+ >
|
|
|
+ <label for="no-sitemap" class="ml-2 text-sm text-gray-500 dark:text-gray-400">
|
|
|
+ No sitemap available
|
|
|
+ </label>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
<div>
|
|
|
<label for="custom-id" class="block text-sm font-medium text-gray-700 dark:text-gray-300">
|
|
|
@@ -171,6 +220,62 @@ export class SitesPage {
|
|
|
urlInput?.focus();
|
|
|
});
|
|
|
|
|
|
+ // URL blur: auto-detect site type
|
|
|
+ const urlInput = this.element?.querySelector('#site-url') as HTMLInputElement;
|
|
|
+ urlInput?.addEventListener('blur', async () => {
|
|
|
+ const url = urlInput.value.trim();
|
|
|
+ if (!url) return;
|
|
|
+ try {
|
|
|
+ new URL(url);
|
|
|
+ } catch {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ const detectSpinner = this.element?.querySelector('#detect-spinner');
|
|
|
+ const detectResult = this.element?.querySelector('#detect-result');
|
|
|
+ const siteTypeSelect = this.element?.querySelector('#site-type-select') as HTMLSelectElement;
|
|
|
+ const sitePlatformSelect = this.element?.querySelector('#site-platform-select') as HTMLSelectElement;
|
|
|
+ const sitemapUrlInput = this.element?.querySelector('#sitemap-url') as HTMLInputElement;
|
|
|
+
|
|
|
+ detectSpinner?.classList.remove('hidden');
|
|
|
+ if (detectResult) {
|
|
|
+ detectResult.classList.add('hidden');
|
|
|
+ detectResult.textContent = '';
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ const response = await this.apiService.detectSite(url);
|
|
|
+ if (response.success && response.data) {
|
|
|
+ const { site_type, site_platform, sitemap_url } = response.data;
|
|
|
+ if (siteTypeSelect) siteTypeSelect.value = site_type;
|
|
|
+ if (sitePlatformSelect) sitePlatformSelect.value = site_platform;
|
|
|
+ if (sitemap_url && sitemapUrlInput) sitemapUrlInput.value = sitemap_url;
|
|
|
+ if (detectResult) {
|
|
|
+ const platformLabel = site_platform === 'generic' ? 'Generic' :
|
|
|
+ site_platform.charAt(0).toUpperCase() + site_platform.slice(1);
|
|
|
+ detectResult.textContent = `Detected: ${site_type} (${platformLabel})${sitemap_url ? ' - sitemap found' : ''}`;
|
|
|
+ detectResult.classList.remove('hidden');
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } catch {
|
|
|
+ // Detection failed silently
|
|
|
+ } finally {
|
|
|
+ detectSpinner?.classList.add('hidden');
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ // No sitemap checkbox
|
|
|
+ const noSitemapCheckbox = this.element?.querySelector('#no-sitemap') as HTMLInputElement;
|
|
|
+ const sitemapInput = this.element?.querySelector('#sitemap-url') as HTMLInputElement;
|
|
|
+ noSitemapCheckbox?.addEventListener('change', () => {
|
|
|
+ if (sitemapInput) {
|
|
|
+ sitemapInput.disabled = noSitemapCheckbox.checked;
|
|
|
+ if (noSitemapCheckbox.checked) {
|
|
|
+ sitemapInput.value = '';
|
|
|
+ }
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
// Hide modal
|
|
|
const hideModal = () => {
|
|
|
modal?.classList.add('hidden');
|
|
|
@@ -197,6 +302,11 @@ export class SitesPage {
|
|
|
const formData = new FormData(form);
|
|
|
const siteUrl = formData.get('siteUrl') as string;
|
|
|
const customId = (formData.get('customId') as string)?.trim() || undefined;
|
|
|
+ const siteType = formData.get('siteType') as string || undefined;
|
|
|
+ const sitePlatform = formData.get('sitePlatform') as string || undefined;
|
|
|
+ const noSitemap = (this.element?.querySelector('#no-sitemap') as HTMLInputElement)?.checked;
|
|
|
+ const sitemapUrlValue = (formData.get('sitemapUrl') as string)?.trim() || undefined;
|
|
|
+ const customSitemapUrl = noSitemap ? undefined : sitemapUrlValue;
|
|
|
|
|
|
if (!siteUrl) return;
|
|
|
|
|
|
@@ -215,7 +325,7 @@ export class SitesPage {
|
|
|
submitSpinner?.classList.remove('hidden');
|
|
|
|
|
|
try {
|
|
|
- const response = await this.apiService.createJob(siteUrl, customId);
|
|
|
+ const response = await this.apiService.createJob(siteUrl, customId, siteType, sitePlatform, customSitemapUrl);
|
|
|
|
|
|
if (response.success) {
|
|
|
this.toastService.success('Site Added', 'Site scraping job has been started');
|
|
|
@@ -313,7 +423,7 @@ export class SitesPage {
|
|
|
|
|
|
private renderSiteCard(site: SiteWithAnalytics): string {
|
|
|
const domain = getDomainFromUrl(site.url);
|
|
|
- const typeInfo = getSiteTypeInfo(site.site_type);
|
|
|
+ const typeInfo = getSiteTypeInfo(site.site_type, site.site_platform);
|
|
|
|
|
|
return `
|
|
|
<div
|