<script setup>
import { ref, watch, onMounted } from "vue";
import BaseInput from "@/components/common/BaseInput.vue";
import DateInput from "@/components/common/DateInput.vue";
import { useRoute, useRouter } from "vue-router";
import { getOptions } from "@/api/default_api_promises";

let route = useRoute()
let router = useRouter()
let commonSearchInput = ref(route.query.common_search_query)
let advancedSearchOn = ref(false)
let formData = ref({})
let metadata = ref({})

// Окна расширенного поиска
const typesOfSearch = [
  {common: "Общий поиск"},
  {image: "Поиск фотодокументов"},
  {audio: "Поиск фонодокументов"},
  {video: "Поиск киновидеодокументов"}
]
let currentSearchWindow = ref("common") // по умолчанию общий поиск

async function loadMetadata() {
  // Load metadata for all material types including common fields
  for (const type of ['image', 'audio', 'video']) {
    const { data } = await getOptions({ url: `/api/${type}-materials/` })();
    metadata.value[type] = formatFields(data.actions.list);
    
    // Get common search fields from the first ViewSet that returns them
    if (!metadata.value.common && data.common_search_fields) {
      metadata.value.common = formatCommonFields(data.common_search_fields);
    }
  }
}

function formatCommonFields(fields) {
  // Convert fields into rows of two items each
  const formattedFields = Object.entries(fields).map(([field, label]) => ({
    name: field,
    placeholder: label
  }));
  
  const rows = [];
  for (let i = 0; i < formattedFields.length; i += 2) {
    rows.push(formattedFields.slice(i, i + 2));
  }
  return rows;
}

function formatFields(listData) {
  return Object.entries(listData)
    .filter(([key]) => key !== 'id')
    .reduce((rows, [key, value]) => {
      if (!rows[rows.length - 1] || rows[rows.length - 1].length === 2) {
        rows.push([{ name: key, placeholder: value.label }]);
      } else {
        rows[rows.length - 1].push({ name: key, placeholder: value.label });
      }
      return rows;
    }, []);
}

function handleSubmit() {
  const minInputLength = 3
  if (advancedSearchOn.value) {
    handleAdvancedSearch();
  } else if (commonSearchInput.value?.length >= minInputLength) {
    handleCommonSearch();
  }
}

function handleCommonSearch() {
  const query = {
    page: 1,
    page_size: 5,
    material_type: route.query.material_type || 'image'
  };

  // Add common search fields to query
  if (commonSearchInput.value) {
    query.common_search_query = commonSearchInput.value;
  }

  // Add any filled form fields
  for (const [key, value] of Object.entries(formData.value)) {
    if (value) {
      query[`${key}__icontains`] = value;
    }
  }

  router.push({ name: "search", query });
}

function handleAdvancedSearch() {
  const query = {
    page: 1,
    page_size: 5,
    material_type: currentSearchWindow.value,
    ...(commonSearchInput.value && { common_search_query: commonSearchInput.value })
  };

  for (const [key, value] of Object.entries(formData.value)) {
    if (value) {
      query[`${key}__icontains`] = value;
    }
  }
  router.push({ name: "search", query });
}

function scrollToOneThirdHeight() {
  const height = window.innerHeight
  window.scrollTo({
    top: height / 2,
    behavior: 'smooth'
  });
}

watch(advancedSearchOn, (newVal) => {
  if (newVal) {
    scrollToOneThirdHeight()
  }
})

onMounted(loadMetadata);
</script>

<template>
  <div class="home">
    <div id="datepicker-container"/>
    <div class="wrapper wrapper__indent">
      <div class="search-box">
        <base-input @keyup.enter="handleSubmit" v-model="commonSearchInput" :value="commonSearchInput" style="width: 400px; padding: 24px;" placeholder="Я ищу"/>
        <v-btn @click="handleSubmit" variant="outlined" class="search-button">
          <v-icon size="28" class="search-icon" color="primary">mdi-magnify</v-icon>
        </v-btn>
      </div>
      <div class="search-switch">
        <v-switch v-model="advancedSearchOn" label="Расширенный поиск" color="primary-blue"/>
      </div>
      <div v-if="advancedSearchOn" class="advanced-search-box">
        <v-window
          v-model="currentSearchWindow"
          show-arrows
          :continuous="true"
          style="width: 1000px;"
        >
          <template v-slot:prev="{ props }">
            <button @click="props.onClick" class="arrow-button prev-arrow">
              <v-icon size="36" color="primary">mdi-chevron-left</v-icon>
            </button>
          </template>
          <template v-slot:next="{ props }">
            <button @click="props.onClick" class="arrow-button next-arrow">
              <v-icon size="36" color="primary">mdi-chevron-right</v-icon>
            </button>
          </template>
          <v-window-item
            v-for="(item, index) in typesOfSearch"
            :key="Object.keys(item)[0]"
            :value="Object.keys(item)[0]"
          >
            <v-card class="d-flex justify-center">
              <div class="search-window">
                <p class="d-block ml-10 mt-2 fs-18 fw-regular color-primary-blue">{{ Object.values(item)[0] }}</p>
                <div class="pl-10 pr-10 pt-3">
                  <v-divider/>
                </div>

                <template v-if="metadata[Object.keys(item)[0]]">
                  <div
                    v-for="(row, rowIndex) in metadata[Object.keys(item)[0]]"
                    :key="rowIndex"
                    class="d-flex justify-space-between mt-5"
                  >
                    <base-input
                      v-for="(field, fieldIndex) in row"
                      :key="field.name"
                      v-model="formData[field.name]"
                      :placeholder="field.placeholder"
                      class="base-input-size"
                      :class="{ 'ml-10': fieldIndex === 0, 'mr-10': fieldIndex === row.length - 1 }"
                    />
                  </div>
                </template>
              </div>
            </v-card>
          </v-window-item>
        </v-window>
      </div>
      <router-view :key="route.fullPath" />
    </div>
  </div>
</template>

<style lang="scss" scoped>
/* Styles remain unchanged */
.search-box {
  display: flex;
  justify-content: center;
  align-items: center;
}

.search-icon {
  height: 60px;
  width: 60px;
  border-radius: 8px;
}

.search-button {
  height: 60px;
  width: 60px;
  border: solid 1px darkgray;
  margin-left: 10px;
}

.search-switch {
  display: flex;
  justify-content: left;
  margin-left: 310px;
}

.advanced-search-box {
  display: flex;
  justify-content: center;
}

.search-window {
  width: 800px;
  height: 250px;
  padding: 15px;
}

.base-input-size {
  width: 340px!important;
  height: 20px!important;
  padding: 15px;
}

v-window-item {
  border: none;
  box-shadow: none;
}
v-window {
  overflow: hidden;
  border: none;
}

.arrow-button {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  opacity: 0;
  transition: opacity 0.4s ease;
  border: none;
  background: transparent;
}

.arrow-button.prev-arrow {
  left: 10px;
}

.arrow-button.next-arrow {
  right: 10px;
}

.advanced-search-box:hover .arrow-button {
  opacity: 1;
}

.item {
  width: 900px;
  padding: 15px;
  margin-top: 15px;
  margin-bottom: 15px;
  border: 1px solid darkgray;
  border-radius: 8px;
  display: flex;
  justify-content: left;
}

.item-label {
  display: block;
  font-size: 12px;
}
</style><script setup>
import { ref, watch } from "vue";
import BaseInput from "@/components/common/BaseInput.vue";
import DateInput from "@/components/common/DateInput.vue";
import { useRoute, useRouter } from "vue-router";


let route = useRoute()
let router = useRouter()
let commonSearchInput = ref(route.query.common_search_query)
let advancedSearchOn = ref(false)
let dateRangeFrom = ref(null)
let dateRangeTo = ref(null)


// Окна расширенного поиска
const typesOfSearch = [
  {common: "Общий поиск"},
  {image: "Поиск фотодокументов"},
  {audio: "Поиск фонодокументов"},
  {video: "Поиск киновидеодокументов"}
]
let currentSearchWindow = ref("common") // по умолчанию общий поиск

function handleSubmit() {
  const minInputLength = 3
  if (commonSearchInput.value.length >= minInputLength) {
    router.push({
      name: "search",
      query: {
        page: 1,
        page_size: 5,
        material_type: route.query.material_type || "image",
        common_search_query: commonSearchInput.value
    }})}
}


function stringToDate(dateString) {
  if (dateString) {
    const [day, month, year] = dateString.split('.')
    return new Date(year, month - 1, day)
  }
}

function scrollToOneThirdHeight() {
  const height = window.innerHeight
  window.scrollTo({
    top: height / 2,
    behavior: 'smooth'
  });
}

watch(advancedSearchOn, (newVal) => {
  if (newVal) {
    scrollToOneThirdHeight()
  }
})

</script>

<template>
  <div class="home">
    <div id="datepicker-container"/>
    <div class="wrapper wrapper__indent">
      <div class="search-box">
        <base-input @keyup.enter="handleSubmit" v-model="commonSearchInput" :value="commonSearchInput" style="width: 400px; padding: 24px;" placeholder="Я ищу"/>
        <v-btn @click="handleSubmit" variant="outlined" class="search-button">
          <v-icon size="28" class="search-icon" color="primary">mdi-magnify</v-icon>
        </v-btn>
      </div>
      <div class="search-switch">
        <v-switch v-model="advancedSearchOn" label="Расширенный поиск" color="primary-blue"/>
      </div>
      <div v-if="advancedSearchOn" class="advanced-search-box">
        <v-window
          v-model="currentSearchWindow"
          show-arrows
          :continuous="true"
          style="width: 1000px;"
        >
          <template v-slot:prev="{ props }">
            <button @click="props.onClick" class="arrow-button prev-arrow">
              <v-icon size="36" color="primary">mdi-chevron-left</v-icon>
            </button>
          </template>
          <template v-slot:next="{ props }">
            <button @click="props.onClick" class="arrow-button next-arrow">
              <v-icon size="36" color="primary">mdi-chevron-right</v-icon>
            </button>
          </template>
          <v-window-item
            v-for="(item, index) in typesOfSearch"
            :key="Object.keys(item)[0]"
            :value="Object.keys(item)[0]"
          >
            <v-card class="d-flex justify-center">
              <div class="search-window">
                <p class="d-block ml-10 mt-2 fs-18 fw-regular color-primary-blue">{{ Object.values(item)[0] }}</p>
                <div class="pl-10 pr-10 pt-3">
                  <v-divider/>
                </div>
                <div class="d-flex justify-space-between mt-5">
                  <base-input class="base-input-size ml-10" placeholder="Название"/>
                  <base-input class="base-input-size mr-10" placeholder="Описание"/>
                </div>
                <div class="d-flex justify-space-between mt-5 pl-10 pr-10" style="height: 30px;">
                  <div class="d-flex justify-space-between">
                    <date-input
                      placeholder="Дата от"
                      teleport="#datepicker-container"
                      v-model="dateRangeFrom"
                      style="width: 155px; margin-right: 13px"
                    />
                    <date-input
                      placeholder="Дата до"
                      teleport="#datepicker-container"
                      :min-date="stringToDate(dateRangeFrom)"
                      v-model="dateRangeTo"
                      style="width: 155px; margin-left: 13px"
                    />
                  </div>
                  <base-input class="base-input-size" placeholder="Архивный номер"/>
                </div>
                <div class="d-flex justify-space-between mt-5" style="height: 30px;">
                  <base-input class="base-input-size ml-10" placeholder="Авторы"/>
                  <base-input class="base-input-size mr-10" placeholder="Персоналии"/>
                </div>
              </div>
            </v-card>
          </v-window-item>
        </v-window>
      </div>
      <router-view :key="route.fullPath" />
    </div>
  </div>
</template>

<style lang="scss" scoped>

.search-box {
  display: flex;
  justify-content: center;
  align-items: center;
}

.search-icon {
  height: 60px;
  width: 60px;
  border-radius: 8px;
}

.search-button {
  height: 60px;
  width: 60px;
  border: solid 1px darkgray;
  margin-left: 10px;
}

.search-switch {
  display: flex;
  justify-content: left;
  margin-left: 310px;
}

.advanced-search-box {
  display: flex;
  justify-content: center;
}

.search-window {
  width: 800px;
  height: 250px;
  padding: 15px;
}

.base-input-size {
  width: 340px!important;
  height: 20px!important;
  padding: 15px;
}

v-window-item {
  border: none;
  box-shadow: none;
}
v-window {
  overflow: hidden;
  border: none;
}

.arrow-button {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  opacity: 0;
  transition: opacity 0.4s ease;
  border: none;
  background: transparent;
}

.arrow-button.prev-arrow {
  left: 10px;
}

.arrow-button.next-arrow {
  right: 10px;
}

.advanced-search-box:hover .arrow-button {
  opacity: 1;
}

.item {
  width: 900px;
  padding: 15px;
  margin-top: 15px;
  margin-bottom: 15px;
  border: 1px solid darkgray;
  border-radius: 8px;
  display: flex;
  justify-content: left;
}

.item-label {
  display: block;
  font-size: 12px;
}


</style>
from rest_framework.metadata import SimpleMetadata class MaterialGetRequestMetadata(SimpleMetadata): def determine_actions(self, request, view): actions = {} action = view.action_map["get"] default_serializer_class = view.get_serializer_class() serializer_class = view.serializer_classes.get(action, default_serializer_class) serializer = serializer_class() actions[action] = self.get_serializer_info(serializer) return actions why common advanced from search is empty fix it. it should look exactly like in static variants but with dynamic adding from metadata like in my other materials. WRITE THE WHOLE MODIFIED VUE COMPONENT WITH DYNAMIC ADDING
IT SHOULDN'T USE STATICALLY WROTEN IN VUE COMPONENT FIELD NAMES