Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

This file was deleted.

Large diffs are not rendered by default.

This file was deleted.

7 changes: 2 additions & 5 deletions resources/dist/build/manifest.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
{
"resources/js/statamic-query-builder.js": {
"file": "assets/statamic-query-builder-BRcJoRQw.js",
"file": "assets/statamic-query-builder-BVPol70U.js",
"name": "statamic-query-builder",
"src": "resources/js/statamic-query-builder.js",
"isEntry": true,
"css": [
"assets/statamic-query-builder-BoX76LBN.css"
]
"isEntry": true
}
}
7 changes: 3 additions & 4 deletions resources/js/components/QueryBuilder/QueryCondition.vue
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
<template>
<div class="flex items-center space-x-4 p-3 rounded-md">
<Select
<Combobox
:model-value="condition.attribute"
:options="fields"
:reduce="field => field.value"
label="label"
class="w-1/3"
:placeholder="__('Select Field')"
:search-keys="['label', 'value']"
@update:model-value="updateAttribute"
/>

Expand Down Expand Up @@ -40,7 +39,7 @@
</template>

<script setup>
import { Select, Button } from '@statamic/cms/ui';
import { Combobox, Select, Button } from '@statamic/cms/ui';
import DateValueInput from './inputs/DateValueInput.vue';
import SelectValueInput from './inputs/SelectValueInput.vue';
import BetweenValueInput from './inputs/BetweenValueInput.vue';
Expand Down
6 changes: 3 additions & 3 deletions resources/js/components/QueryBuilder/QueryGroup.vue
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,10 @@
:reduce="option => option.value"
label="label"
:placeholder="__('Add...')"
class="w-40"
class="w-56"
:adaptive-width="true"
@update:model-value="handleAddSelection"
>
</Select>
/>
<div class="flex items-center space-x-1">
<Button
v-if="canMoveUp"
Expand Down
261 changes: 113 additions & 148 deletions resources/js/components/fieldtypes/QueryBuilder.vue
Original file line number Diff line number Diff line change
@@ -1,103 +1,92 @@
<template>
<div class="query-builder max-w-4xl mx-auto">
<div class="query-builder space-y-4">
<Card>
<div class="space-y-4">
<div class="flex flex-wrap items-end gap-4">
<Field v-if="builderTemplates" :label="__('Template')" class="w-48">
<Select
name="builderTemplate"
:model-value="builderTemplate"
:options="builderTemplates"
:adaptive-width="true"
@update:model-value="(val) => { builderTemplate = val; updateSettingValues(); }"
/>
</Field>

<Field v-if="isLimitVisible" :label="__('Limit results')" class="w-32">
<Input
id="limit"
type="number"
name="limit"
:model-value="limit"
min="1"
@update:model-value="(val) => { limit = parseInt(val) || props.defaultLimit; updateSettingValues(); }"
/>
</Field>

<Field v-if="sortFields" :label="__('Sort')" class="min-w-72 flex-1">
<div class="flex gap-2">
<Select
name="sortField"
:model-value="sortField"
:options="sortFields"
:placeholder="__('Select...')"
:adaptive-width="true"
class="min-w-0 flex-1"
@update:model-value="(val) => { sortField = val; updateSettingValues(); }"
/>
<Select
name="sortDirection"
:model-value="sortDirection"
:options="sortDirections"
class="w-36 shrink-0"
@update:model-value="(val) => { sortDirection = val; updateSettingValues(); }"
/>
</div>
</Field>

<Field v-if="groupedPresets.length > 0" :label="__('Presets')" class="min-w-64 flex-1">
<Select
:model-value="selectedPresetKey"
:options="groupedPresets"
:placeholder="__('Select Preset')"
:adaptive-width="true"
@update:model-value="handlePresetSelection"
/>
</Field>
</div>

<div class="p-4 border mb-6 rounded">
<div class="flex flex-wrap items-end gap-4 justify-between">
<Field v-if="showUseDefaultQueryToggle" class="mb-2">
<div class="flex flex-wrap items-center gap-4">
<Checkbox
v-if="showUseDefaultQueryToggle"
id="useDefaultQuery"
:label="__('Use default Query')"
v-model="useDefaultQuery"
@change="(e) => { useDefaultQuery = e.target.checked; updateSettingValues(); }"
/>
</Field>

<div v-if="showUseDefaultQueryToggle" class="h-8 border-r mx-2 hidden md:block"></div>

<Field v-if="builderTemplates">
<Label for="template" class="text-2xs uppercase tracking-wider mb-1">{{ __('Template') }}</Label>
<Select
name="builderTemplate"
:model-value="builderTemplate"
:options="builderTemplates"
:reduce="field => field.value"
class="w-40"
@update:model-value="(val) => { builderTemplate = val; updateSettingValues(); }"
/>
</Field>

<Field v-if="isLimitVisible">
<Label for="limit" class="text-xs uppercase tracking-wider mb-1 leading-none h-3 whitespace-nowrap">{{ __('Limit results') }}</Label>
<Input
type="number"
name="limit"
id="limit"
:value="limit"
class="w-24"
min="1"
@input="(e) => { limit = parseInt(e.target.value) || props.defaultLimit; updateSettingValues(); }"
/>
</Field>

<div class="h-8 border-r mx-2 hidden md:block"></div>

<Field v-if="sortFields">
<Label for="sortField" class="text-2xs uppercase tracking-wider mb-1">{{ __('Sort') }}</Label>
<div class="flex gap-1">
<Select
name="sortField"
:model-value="sortField"
:options="sortFields"
:reduce="field => field.value"
class="w-40"
@update:model-value="(val) => { sortField = val; updateSettingValues(); }"
/>
<Select
name="sortDirection"
:model-value="sortDirection"
:options="sortDirections"
class="w-24"
@update:model-value="(val) => { sortDirection = val; updateSettingValues(); }"
/>
</div>
</Field>

<Field v-if="groupedPresets.length > 0">
<Label class="text-2xs uppercase tracking-wider mb-1">{{ __('Presets') }}</Label>
<Select
:model-value="selectedPresetKey"
:options="groupedPresets"
:reduce="preset => preset.value"
:placeholder="__('Select Preset')"
class="w-64"
@update:model-value="handlePresetSelection"
@update:model-value="updateSettingValues"
/>
</Field>

<div class="flex-grow"></div>

<Button class="btn-primary" @click="addGroup">
<span class="flex items-center">
<svg class="w-4 h-4 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6v6m0 0v6m0-6h6m-6 0H6" />
</svg>
<Button
class="ms-auto"
variant="primary"
icon="plus"
@click="addGroup"
>
{{ __('Add Group') }}
</span>
</Button>
</Button>
</div>
</div>
</div>
</Card>

<div class="space-y-6">
<div class="space-y-4">
<div v-if="groups.length > 0" class="flex justify-center">
<Button
class="insert-group-btn"
@click="insertGroupAt(0)"
variant="subtle"
size="sm"
round
icon="plus"
:title="__('Insert group here')"
>
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6v6m0 0v6m0-6h6m-6 0H6" />
</svg>
</Button>
@click="insertGroupAt(0)"
/>
</div>

<template v-for="(group, groupIndex) in groups" :key="`group-${groupIndex}`">
Expand Down Expand Up @@ -125,72 +114,58 @@
@remove-nested-condition="removeNestedCondition"
/>

<div v-if="groupIndex < groups.length" :key="`separator-${groupIndex}`" class="flex flex-col items-center space-y-2">
<div class="flex justify-center">
<Button
class="insert-group-btn"
@click="insertGroupAt(groupIndex + 1)"
variant="subtle"
size="sm"
round
icon="plus"
:title="__('Insert group here')"
>
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6v6m0 0v6m0-6h6m-6 0H6" />
</svg>
</Button>
@click="insertGroupAt(groupIndex + 1)"
/>
</div>
</template>
</div>

<div v-if="!groups.length" class="text-center py-8 bg-gray-50 rounded-lg">
<p class="text-gray-500 mb-4">{{ __('No groups added yet. Click "Add Group" to start building your query.') }}</p>
</div>

<div v-if="groups.length > 1" class="mt-6 pt-4 border-t border-gray-200">
<Field>
<Label class="font-bold">{{ __('Combine Groups with:') }}</Label>
<Select
:model-value="globalConjunction"
:options="logicalOperators"
class="w-32"
@update:model-value="(val) => { globalConjunction = val; updateValue(); }"
/>
</Field>
</div>

<div v-if="showConflictModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
<div class="bg-white p-6 rounded-lg shadow-lg max-w-md w-full mx-4">
<h3 class="text-lg font-semibold mb-4">{{ __('Apply Preset') }}</h3>
<p class="text-gray-600 mb-6">
{{ __('You have an existing query. How would you like to apply this preset?') }}
</p>
<div class="flex space-x-3">
<Button
class="btn flex-1"
@click="applyPreset('merge')"
>
{{ __('Merge') }}
</Button>
<Button
class="btn-primary flex-1"
@click="applyPreset('override')"
>
{{ __('Override') }}
</Button>
<Button
class="btn flex-1"
@click="cancelPresetApplication"
>
{{ __('Cancel') }}
</Button>
<Card v-if="!groups.length">
<Description>
{{ __('No groups added yet. Click "Add Group" to start building your query.') }}
</Description>
</Card>

<Field v-if="groups.length > 1" :label="__('Combine Groups with:')" class="w-40">
<Select
:model-value="globalConjunction"
:options="logicalOperators"
@update:model-value="(val) => { globalConjunction = val; updateValue(); }"
/>
</Field>

<Modal
:open="showConflictModal"
:title="__('Apply Preset')"
@update:open="(open) => { if (!open) cancelPresetApplication(); }"
@dismissed="cancelPresetApplication"
>
<Description>
{{ __('You have an existing query. How would you like to apply this preset?') }}
</Description>
<template #footer>
<div class="flex items-center justify-end space-x-3 pt-3 pb-1">
<Button @click="cancelPresetApplication">{{ __('Cancel') }}</Button>
<Button @click="applyPreset('merge')">{{ __('Merge') }}</Button>
<Button variant="primary" @click="applyPreset('override')">{{ __('Override') }}</Button>
</div>
</div>
</div>
</template>
</Modal>
</div>
</template>

<script setup>
import { ref, computed, onMounted, getCurrentInstance } from 'vue';
import axios from 'axios';
import QueryGroup from '../QueryBuilder/QueryGroup.vue';
import { Select, Input, Field, Label, Checkbox, Button } from '@statamic/cms/ui';
import { Select, Input, Field, Checkbox, Button, Card, Modal, Description } from '@statamic/cms/ui';

const props = defineProps({
fields: {
Expand Down Expand Up @@ -771,13 +746,3 @@ onMounted(() => {
fetchPresets();
});
</script>

<style scoped>
.insert-group-btn {
@apply flex items-center justify-center w-8 h-8 bg-blue-50 border-2 border-dashed border-blue-300 rounded-full text-blue-500 hover:bg-blue-100 hover:border-blue-400 transition-colors duration-200;
}

.insert-group-btn:hover {
@apply shadow-sm;
}
</style>
4 changes: 3 additions & 1 deletion src/Http/Controllers/CP/ProductAttributeController.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ public function index()
return [];
}

return $model::with('attributeOptions')->get();
return $model::with('attributeOptions')
->filterable()
->get();
Comment thread
kevinmeijer97 marked this conversation as resolved.
}
}
9 changes: 9 additions & 0 deletions src/Models/ProductAttribute.php
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,15 @@ public function attributeOptions(): HasMany
return $this->hasMany(ProductAttributeOption::class, 'attribute_id', 'attribute_id');
}

public function scopeFilterable(Builder $query): Builder
{
return $query->whereIn('eav_attribute.attribute_id', function ($subQuery) {
$subQuery->select('attribute_id')
->from('catalog_eav_attribute')
->where('is_filterable', '>', 0);
});
}

public function scopeRunwaySearch(Builder $query, string $search): Builder
{
return $query->where(function ($query) use ($search) {
Expand Down
Loading