[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of b2b-quotes/messages/sv.json from the b2b-quotes VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "admin/b2b-quotes.navigation.label": "B2B Offerter",
  "admin/b2b-quotes.navigation.keywords": "b2B, offert, offerter, varukorg, varukorgar, utgångsperiod",
  "admin/b2b-quotes.settings.title": "B2B Offertinställningar",
  "admin/b2b-quotes.settings.saveSettings.failure": "Det gick inte att spara inställningarna",
  "admin/b2b-quotes.settings.saveSettings.success": "Inställningar har sparats",
  "admin/b2b-quotes.settings.cartLifeSpan.label": "Standard utgångsperiod (i dagar) för offerter och sparade varukorgar",
  "admin/b2b-quotes.settings.saveSettings.management.quotes.expiration.data": "Expiration Period for Quotes and Saved Carts",
  "admin/b2b-quotes.settings.saveSettings.management.quotes.title": "Quote Management for Marketplace",
  "admin/b2b-quotes.settings.saveSettings.management.quotes.description": "Configure how quotes are managed in the marketplace.",
  "admin/b2b-quotes.settings.saveSettings.management.quotes.info": "Who is responsible for managing quotes?",
  "admin/b2b-quotes.settings.saveSettings.management.quotes.option.one": "Marketplace",
  "admin/b2b-quotes.settings.saveSettings.management.quotes.option.one.describe": "Only the main marketplace manages quotes.",
  "admin/b2b-quotes.settings.saveSettings.management.quotes.option.two": "Sellers",
  "admin/b2b-quotes.settings.saveSettings.management.quotes.option.two.describe": "Sellers manage quotes for their own products and can choose not to account quotes.",
  "admin/b2b-quotes.settings.saveSettings.buttonText": "Spara inställningar",
  "store/b2b-quotes.myAccountLink": "Offerter och sparade varukorgar",
  "store/b2b-quotes.create.title": "Skapa offert",
  "store/b2b-quotes.create.nameLabel": "Namn",
  "store/b2b-quotes.create.descriptionLabel": "Anteckningar",
  "store/b2b-quotes.create.success": "Offert sparades",
  "store/b2b-quotes.create.error": "Det gick inte att spara offert",
  "store/b2b-quotes.create.required": "Ange ett namn för din offert",
  "store/b2b-quotes.create.placeholder.quotationName": "Offertnamn",
  "store/b2b-quotes.create.empty-state-label": "För att skapa en offert, börja lägga till artiklar i din varukorg.",
  "store/b2b-quotes.create.characterLeft": "{count, plural, =499{tecken kvar}=500{500 tecken nådde}other{tecken kvar}}",
  "store/b2b-quotes.create.button.save-for-later": "Spara till senare",
  "store/b2b-quotes.create.button.request-quote": "Begär offert",
  "store/b2b-quotes.back": "Återgå",
  "store/b2b-quotes.error.notAuthenticated": "Logga in för att se detta innehåll.",
  "store/b2b-quotes.error.notPermitted": "Du har inte behörighet att se detta innehåll.",
  "store/b2b-quotes.quotes-table.title": "Mina offerter",
  "store/b2b-quotes.quotes-table.newLine.label": "Ny offert",
  "store/b2b-quotes.quotes-table.empty-state-label": "Inga offerter hittades.",
  "store/b2b-quotes.quotes-table.details.label": "Detaljer",
  "store/b2b-quotes.quotes-table.referenceName.title": "Ref. Namn",
  "store/b2b-quotes.quotes-table.subtotal.title": "Delbelopp",
  "store/b2b-quotes.quotes-table.creatorEmail.title": "Skapad av",
  "store/b2b-quotes.quotes-table.creationDate.title": "Skapad den",
  "store/b2b-quotes.quotes-table.expirationDate.title": "Utgångsdatum",
  "store/b2b-quotes.quotes-table.status.title": "Status",
  "store/b2b-quotes.quotes-table.lastUpdate.title": "Senast uppdaterad",
  "store/b2b-quotes.quotes-table.organization.title": "Organisation",
  "store/b2b-quotes.quotes-table.costCenter.title": "Kostnadsställe",
  "store/b2b-quotes.quotes-table.orgAndCostCenter.title": "Org. & Kostnadsställe",
  "store/b2b-quotes.quotes-table.searchOrganization.title": "Sök i organisationer...",
  "store/b2b-quotes.quotes-table.searchCostCenter.title": "Sök i kostnadsställe...",
  "store/b2b-quotes.quotes-table.showRows": "Visa rader",
  "store/b2b-quotes.quotes-table.of": "av",
  "store/b2b-quotes.quotes-table.search.placeholder": "Sök",
  "store/b2b-quotes.quotes-table.toggleFields.label": "Växla synliga fält",
  "store/b2b-quotes.quotes-table.toggleFields.showAllLabel": "Visa alla",
  "store/b2b-quotes.quotes-table.toggleFields.hideAllLabel": "Dölj alla",
  "store/b2b-quotes.quotes-table.clearFilters.label": "Rensa filter",
  "store/b2b-quotes.quotes-table.statusFilter.label": "Status",
  "store/b2b-quotes.quotes-table.filters.all": "Alla",
  "store/b2b-quotes.quotes-table.filters.none": "Ingen",
  "store/b2b-quotes.quotes-table.filters.any": "Någon",
  "store/b2b-quotes.quotes-table.filters.is": "är",
  "store/b2b-quotes.quotes-table.filters.includes": "inkluderar",
  "store/b2b-quotes.quote-details.title": "Offertuppgifter",
  "store/b2b-quotes.quote-details.decline": "Avvisa",
  "store/b2b-quotes.quote-details.save": "Spara offert",
  "store/b2b-quotes.quote-details.submit-to-sales-rep": "Skicka till försäljningsagent",
  "store/b2b-quotes.quote-details.use-quote": "Använd offert",
  "store/b2b-quotes.quote-details.subtotal.title": "Delbelopp",
  "store/b2b-quotes.quote-details.expiration.title": "Utgångsdatum",
  "store/b2b-quotes.quote-details.status.title": "Status",
  "store/b2b-quotes.quote-details.items.image.title": "Bild",
  "store/b2b-quotes.quote-details.items.refId.title": "Ref. Kod",
  "store/b2b-quotes.quote-details.items.name.title": "Namn",
  "store/b2b-quotes.quote-details.items.quantity.title": "Kvantitet",
  "store/b2b-quotes.quote-details.items.price.title": "Pris",
  "store/b2b-quotes.quote-details.items.total.title": "Totalt",
  "store/b2b-quotes.quote-details.apply-discount.title": "Tillämpa procentrabatt",
  "store/b2b-quotes.quote-details.apply-discount.help-text": "Tillämpa en procentuell rabatt på offerten delsumman. Obs: Detta kommer att åsidosätta eventuella ändringar i enskilda SKU priser.",
  "store/b2b-quotes.quote-details.apply-discount.disabled-message": "Procentrabatt är inaktiverad eftersom en kampanj redan tillämpar en rabatt högre än den maximala manuella rabatten.",
  "store/b2b-quotes.quote-details.apply-discount.maxDiscount-text": "Manuella rabatter över {maxDiscount}% kommer automatiskt att avvisas.",
  "store/b2b-quotes.quote-details.products.title": "Produkter ({total})",
  "store/b2b-quotes.quote-details.notes-after-created.helpText": "Anteckningar kan läggas till efter att offerten har skapats.",
  "store/b2b-quotes.quote-details.update-history.title": "Uppdatera historik",
  "store/b2b-quotes.quote-details.update-history.update-details": "{index, plural, =0{Skapad}other{Uppdaterad}} den {date} av {email} ({role}) med status {status}",
  "store/b2b-quotes.quote-details.update-history.notes": "Anteckningar:",
  "store/b2b-quotes.quote-details.add-note.label": "Lägg till anteckning",
  "store/b2b-quotes.quote-details.update-success": "Offert sparades",
  "store/b2b-quotes.quote-details.update-error": "Kunde inte spara offert. Se konsolen för mer information.",
  "store/b2b-quotes.quote-details.use-error": "Kunde inte använda offert. Se konsolen för mer information.",
  "store/b2b-quotes.quote-details.discount-error": "Vissa rabatter är högre än tillåtna",
  "store/b2b-quotes.quote-details.percentage-discount.title": "Procentuell rabatt",
  "store/b2b-quotes.quote-details.original-subtotal.title": "Ursprunglig delsumma",
  "store/b2b-quotes.quote-details.original-price.title": "Ursprungspris",
  "store/b2b-quotes.quote-details.quote-price.title": "Offererat pris",
  "store/b2b-quotes.quote-details.quoted-subtotal.title": "Offererad delsumma",
  "store/b2b-quotes.quote-details.expiration-date-change.title": "Ändra förfallodatum",
  "store/b2b-quotes.quote-locking.title": "Offert i bruk",
  "store/b2b-quotes.quote-locking.message": "Din varukorg innehåller för närvarande artiklar från en offert och kan inte ändras. Du kan antingen återvända till kassan eller rensa din kundvagn för att fortsätta surfa.",
  "store/b2b-quotes.quote-locking.return-to-checkout": "Gå tillbaka till kassan",
  "store/b2b-quotes.quote-locking.clear-cart": "Töm varukorgen",
  "store/b2b-quotes.quote-status.ready": "Klar",
  "store/b2b-quotes.quote-status.placed": "Placerad",
  "store/b2b-quotes.quote-status.revised": "Reviderad",
  "store/b2b-quotes.quote-status.pending": "Väntande",
  "store/b2b-quotes.quote-status.expired": "Utgått",
  "store/b2b-quotes.quote-status.declined": "Avvisad"
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of b2b-quotes/messages/es.json from the b2b-quotes VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "admin/b2b-quotes.navigation.label": "Cotizaciones B2B",
  "admin/b2b-quotes.navigation.keywords": "b2b, cotización, cotizaciones, carrito, carritos, vencimiento",
  "admin/b2b-quotes.settings.title": "Configuración de cotizaciones B2B",
  "admin/b2b-quotes.settings.saveSettings.failure": "No se pudo guardar la configuración",
  "admin/b2b-quotes.settings.saveSettings.success": "Configuración guardada con éxito",
  "admin/b2b-quotes.settings.cartLifeSpan.label": "Periodo de vencimiento predeterminado (en días) para cotizaciones y carritos guardados",
  "admin/b2b-quotes.settings.saveSettings.management.quotes.expiration.data": "Expiration Period for Quotes and Saved Carts",
  "admin/b2b-quotes.settings.saveSettings.management.quotes.title": "Gestión de cotizaciones para el mercado",
  "admin/b2b-quotes.settings.saveSettings.management.quotes.description": "Configura cómo se gestionan las cotizaciones en el mercado.",
  "admin/b2b-quotes.settings.saveSettings.management.quotes.info": "¿Quién es responsable de gestionar las cotizaciones?",
  "admin/b2b-quotes.settings.saveSettings.management.quotes.option.one": "Mercado",
  "admin/b2b-quotes.settings.saveSettings.management.quotes.option.one.describe": "Solo el mercado principal gestiona las cotizaciones.",
  "admin/b2b-quotes.settings.saveSettings.management.quotes.option.two": "Vendedores",
  "admin/b2b-quotes.settings.saveSettings.management.quotes.option.two.describe": "Los vendedores gestionan las cotizaciones de sus propios productos y pueden elegir no contabilizarlas.",
  "admin/b2b-quotes.settings.saveSettings.buttonText": "Guardar configuración",
  "store/b2b-quotes.myAccountLink": "Cotizaciones y carritos guardados",
  "store/b2b-quotes.create.title": "Crear cotización",
  "store/b2b-quotes.create.nameLabel": "Nombre",
  "store/b2b-quotes.create.descriptionLabel": "Observaciones",
  "store/b2b-quotes.create.success": "Cotización guardada con éxito",
  "store/b2b-quotes.create.error": "Error al guardar la cotización",
  "store/b2b-quotes.create.required": "Ingresa un nombre para tu cotización",
  "store/b2b-quotes.create.placeholder.quotationName": "Nombre de la cotización",
  "store/b2b-quotes.create.empty-state-label": "Agrega ítems a tu carrito para crear una cotización",
  "store/b2b-quotes.create.characterLeft": "{count, plural, one {} =499{caracter restante}=500{Límite de 500 caracteres alcanzado}other{caracteres restantes}}",
  "store/b2b-quotes.create.button.save-for-later": "Guardar para después",
  "store/b2b-quotes.create.button.request-quote": "Solicitar cotización",
  "store/b2b-quotes.back": "Atrás",
  "store/b2b-quotes.error.notAuthenticated": "Por favor, inicia sesión para ver este contenido.",
  "store/b2b-quotes.error.notPermitted": "No tienes el permiso necesario para ver este contenido.",
  "store/b2b-quotes.quotes-table.title": "Mis cotizaciones",
  "store/b2b-quotes.quotes-table.newLine.label": "Nueva cotización",
  "store/b2b-quotes.quotes-table.empty-state-label": "No se encontraron cotizaciones",
  "store/b2b-quotes.quotes-table.details.label": "Detalles",
  "store/b2b-quotes.quotes-table.referenceName.title": "Nombre de ref.",
  "store/b2b-quotes.quotes-table.subtotal.title": "Subtotal",
  "store/b2b-quotes.quotes-table.creatorEmail.title": "Creada por",
  "store/b2b-quotes.quotes-table.creationDate.title": "Creada el",
  "store/b2b-quotes.quotes-table.expirationDate.title": "Fecha de vencimiento",
  "store/b2b-quotes.quotes-table.status.title": "Status",
  "store/b2b-quotes.quotes-table.lastUpdate.title": "Última actualización",
  "store/b2b-quotes.quotes-table.organization.title": "Organización",
  "store/b2b-quotes.quotes-table.costCenter.title": "Centro de costos",
  "store/b2b-quotes.quotes-table.orgAndCostCenter.title": "Org. y centro de costo",
  "store/b2b-quotes.quotes-table.searchOrganization.title": "Buscar organizaciones...",
  "store/b2b-quotes.quotes-table.searchCostCenter.title": "Buscar centros de costos...",
  "store/b2b-quotes.quotes-table.showRows": "Mostrar filas",
  "store/b2b-quotes.quotes-table.of": "de",
  "store/b2b-quotes.quotes-table.search.placeholder": "Buscar",
  "store/b2b-quotes.quotes-table.toggleFields.label": "Alternar campos visibles",
  "store/b2b-quotes.quotes-table.toggleFields.showAllLabel": "Mostrar todo",
  "store/b2b-quotes.quotes-table.toggleFields.hideAllLabel": "Ocultar todo",
  "store/b2b-quotes.quotes-table.clearFilters.label": "Limpiar filtros",
  "store/b2b-quotes.quotes-table.statusFilter.label": "Status",
  "store/b2b-quotes.quotes-table.filters.all": "Todos",
  "store/b2b-quotes.quotes-table.filters.none": "Ninguno",
  "store/b2b-quotes.quotes-table.filters.any": "Cualquiera",
  "store/b2b-quotes.quotes-table.filters.is": "es",
  "store/b2b-quotes.quotes-table.filters.includes": "incluye",
  "store/b2b-quotes.quote-details.title": "Información de la cotización",
  "store/b2b-quotes.quote-details.decline": "Rechazar",
  "store/b2b-quotes.quote-details.save": "Guardar cotización",
  "store/b2b-quotes.quote-details.submit-to-sales-rep": "Enviar al vendedor",
  "store/b2b-quotes.quote-details.use-quote": "Usar cotización",
  "store/b2b-quotes.quote-details.subtotal.title": "Subtotal",
  "store/b2b-quotes.quote-details.expiration.title": "Fecha de vencimiento",
  "store/b2b-quotes.quote-details.status.title": "Status",
  "store/b2b-quotes.quote-details.items.image.title": "Imagen",
  "store/b2b-quotes.quote-details.items.refId.title": "Cód. de ref.",
  "store/b2b-quotes.quote-details.items.name.title": "Nombre",
  "store/b2b-quotes.quote-details.items.quantity.title": "Cantidad",
  "store/b2b-quotes.quote-details.items.price.title": "Precio",
  "store/b2b-quotes.quote-details.items.total.title": "Total",
  "store/b2b-quotes.quote-details.apply-discount.title": "Aplicar descuento porcentual",
  "store/b2b-quotes.quote-details.apply-discount.help-text": "Aplica un descuento porcentual al subtotal de la cotización. Observación: esto reemplazará cualquier cambio realizado en los precios de SKU individuales.",
  "store/b2b-quotes.quote-details.apply-discount.disabled-message": "El descuento porcentual está desactivado porque hay una promoción que ya ha aplicado un descuento mayor que el descuento manual máximo.",
  "store/b2b-quotes.quote-details.apply-discount.maxDiscount-text": "Los descuentos manuales superiores al {maxDiscount} % se rechazarán automáticamente.",
  "store/b2b-quotes.quote-details.products.title": "Productos ({total})",
  "store/b2b-quotes.quote-details.notes-after-created.helpText": "Se pueden agregar notas después de crear las cotizaciones.",
  "store/b2b-quotes.quote-details.update-history.title": "Historial de actualizaciones",
  "store/b2b-quotes.quote-details.update-history.update-details": "{index, plural, one {} =0{Creada}other{Actualizada}} el {date} por {email} ({role}) con el status {status}",
  "store/b2b-quotes.quote-details.update-history.notes": "Observaciones:",
  "store/b2b-quotes.quote-details.add-note.label": "Agregar observación",
  "store/b2b-quotes.quote-details.update-success": "Cotización guardada con éxito.",
  "store/b2b-quotes.quote-details.update-error": "Error al guardar la cotización. Consulta la consola para ver más detalles.",
  "store/b2b-quotes.quote-details.use-error": "Error al usar la cotización. Consulta la consola para ver más detalles.",
  "store/b2b-quotes.quote-details.discount-error": "Hay algunos descuentos que son superiores al máximo permitido.",
  "store/b2b-quotes.quote-details.percentage-discount.title": "Descuento porcentual",
  "store/b2b-quotes.quote-details.original-subtotal.title": "Subtotal original",
  "store/b2b-quotes.quote-details.original-price.title": "Precio original",
  "store/b2b-quotes.quote-details.quote-price.title": "Precio cotizado",
  "store/b2b-quotes.quote-details.quoted-subtotal.title": "Subtotal cotizado",
  "store/b2b-quotes.quote-details.expiration-date-change.title": "Modificar fecha de vencimiento",
  "store/b2b-quotes.quote-locking.title": "Cotización en uso",
  "store/b2b-quotes.quote-locking.message": "Tu carrito contiene ítems de una cotización y no puede ser modificado. Puedes volver a la página de checkout o limpiar el carrito para continuar navegando.",
  "store/b2b-quotes.quote-locking.return-to-checkout": "Volver al checkout",
  "store/b2b-quotes.quote-locking.clear-cart": "Limpiar carrito",
  "store/b2b-quotes.quote-status.ready": "Lista",
  "store/b2b-quotes.quote-status.placed": "Realizada",
  "store/b2b-quotes.quote-status.revised": "Revisada",
  "store/b2b-quotes.quote-status.pending": "Pendiente",
  "store/b2b-quotes.quote-status.expired": "Vencida",
  "store/b2b-quotes.quote-status.declined": "Rechazada"
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of b2b-quotes/messages/ar.json from the b2b-quotes VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "admin/b2b-quotes.navigation.label": "أسعار B2B",
  "admin/b2b-quotes.navigation.keywords": "b2b، تسعير، أسعار، عربة، عربات، انتهاء الصلاحية",
  "admin/b2b-quotes.settings.title": "إعدادات تسعير B2B",
  "admin/b2b-quotes.settings.saveSettings.failure": "فشل في حفظ الإعدادات",
  "admin/b2b-quotes.settings.saveSettings.success": "تم حفظ الإعدادات بنجاح",
  "admin/b2b-quotes.settings.cartLifeSpan.label": "فترة انتهاء الصلاحية الافتراضية (بالأيام) للأسعار والعربات المسجلة",
  "admin/b2b-quotes.settings.saveSettings.management.quotes.expiration.data": "Expiration Period for Quotes and Saved Carts",
  "admin/b2b-quotes.settings.saveSettings.management.quotes.title": "إدارة العروض للسوق",
  "admin/b2b-quotes.settings.saveSettings.management.quotes.description": "قم بتكوين كيفية إدارة العروض في السوق.",
  "admin/b2b-quotes.settings.saveSettings.management.quotes.info": "من المسؤول عن إدارة العروض؟",
  "admin/b2b-quotes.settings.saveSettings.management.quotes.option.one": "السوق",
  "admin/b2b-quotes.settings.saveSettings.management.quotes.option.one.describe": "يدير السوق الرئيسي فقط العروض.",
  "admin/b2b-quotes.settings.saveSettings.management.quotes.option.two": "البائعون",
  "admin/b2b-quotes.settings.saveSettings.management.quotes.option.two.describe": "يدير البائعون العروض الخاصة بمنتجاتهم ويمكنهم اختيار عدم حساب العروض.",
  "admin/b2b-quotes.settings.saveSettings.buttonText": "حفظ الإعدادات",
  "store/b2b-quotes.myAccountLink": "عروض الأسعار والعربات المسجلة",
  "store/b2b-quotes.create.title": "إنشاء عرض أسعار",
  "store/b2b-quotes.create.nameLabel": "الإسم",
  "store/b2b-quotes.create.descriptionLabel": "ملاحظات",
  "store/b2b-quotes.create.success": "تم حفظ عرض الأسعار بنجاح",
  "store/b2b-quotes.create.error": "حدث خطأ أثناء حفظ الاسعار",
  "store/b2b-quotes.create.required": "أدخل اسما لعرض الأسعار الخاص بك",
  "store/b2b-quotes.create.placeholder.quotationName": "اسم عرض الأسعار",
  "store/b2b-quotes.create.empty-state-label": "لإنشاء عرض أسعار، ابدأ بإضافة عناصر إلى عربة التسوق الخاصة بك.",
  "store/b2b-quotes.create.characterLeft": "{count, plural, =499{حرفًا متبقيًا}=500{تم الوصول لحد عدد الأحرف الـ 500}other{حرفًا متبقيًا}}",
  "store/b2b-quotes.create.button.save-for-later": "حفظ لوقت لاحق",
  "store/b2b-quotes.create.button.request-quote": "طلب عرض الأسعار",
  "store/b2b-quotes.back": "عودة",
  "store/b2b-quotes.error.notAuthenticated": "الرجاء تسجيل الدخول لعرض هذا المحتوى.",
  "store/b2b-quotes.error.notPermitted": "ليس لديك الصلاحيات اللازمة لعرض هذا المحتوى.",
  "store/b2b-quotes.quotes-table.title": "عروض الأسعار الخاصة بي",
  "store/b2b-quotes.quotes-table.newLine.label": "عرض أسعار جديد",
  "store/b2b-quotes.quotes-table.empty-state-label": "لم يتم العثور على عرض أسعار.",
  "store/b2b-quotes.quotes-table.details.label": "التفاصبل",
  "store/b2b-quotes.quotes-table.referenceName.title": "اسم المرجع",
  "store/b2b-quotes.quotes-table.subtotal.title": "تكلفة الطلب",
  "store/b2b-quotes.quotes-table.creatorEmail.title": "انشأ من قبل",
  "store/b2b-quotes.quotes-table.creationDate.title": "تم إنشاؤه في",
  "store/b2b-quotes.quotes-table.expirationDate.title": "تاريخ انتهاء الصلاحية",
  "store/b2b-quotes.quotes-table.status.title": "الحالة",
  "store/b2b-quotes.quotes-table.lastUpdate.title": "آخر تحديث",
  "store/b2b-quotes.quotes-table.organization.title": "المنظمة",
  "store/b2b-quotes.quotes-table.costCenter.title": "مركز التكلفة",
  "store/b2b-quotes.quotes-table.orgAndCostCenter.title": "منظمة ومركز التكلفة",
  "store/b2b-quotes.quotes-table.searchOrganization.title": "البحث عن منظمات...",
  "store/b2b-quotes.quotes-table.searchCostCenter.title": "البحث عن مراكز التكلفة...",
  "store/b2b-quotes.quotes-table.showRows": "عرض الخانات",
  "store/b2b-quotes.quotes-table.of": "من",
  "store/b2b-quotes.quotes-table.search.placeholder": "بحث",
  "store/b2b-quotes.quotes-table.toggleFields.label": "تبديل الحقول المرئية",
  "store/b2b-quotes.quotes-table.toggleFields.showAllLabel": "عرض الكل",
  "store/b2b-quotes.quotes-table.toggleFields.hideAllLabel": "إخفاء الكل",
  "store/b2b-quotes.quotes-table.clearFilters.label": "مسح المرشحات",
  "store/b2b-quotes.quotes-table.statusFilter.label": "الحالة",
  "store/b2b-quotes.quotes-table.filters.all": "الكل",
  "store/b2b-quotes.quotes-table.filters.none": "لا شيء",
  "store/b2b-quotes.quotes-table.filters.any": "أي",
  "store/b2b-quotes.quotes-table.filters.is": "هو",
  "store/b2b-quotes.quotes-table.filters.includes": "يشمل",
  "store/b2b-quotes.quote-details.title": "تفاصيل عرض الأسعار",
  "store/b2b-quotes.quote-details.decline": "رفض",
  "store/b2b-quotes.quote-details.save": "حفظ عرض الأسعار",
  "store/b2b-quotes.quote-details.submit-to-sales-rep": "إرسال إلى مسؤول المبيعات",
  "store/b2b-quotes.quote-details.use-quote": "استخدام عرض الأسعار",
  "store/b2b-quotes.quote-details.subtotal.title": "تكلفة الطلب",
  "store/b2b-quotes.quote-details.expiration.title": "تاريخ انتهاء الصلاحية",
  "store/b2b-quotes.quote-details.status.title": "الحالة",
  "store/b2b-quotes.quote-details.items.image.title": "الصورة",
  "store/b2b-quotes.quote-details.items.refId.title": "الرمز المرجعي",
  "store/b2b-quotes.quote-details.items.name.title": "الإسم",
  "store/b2b-quotes.quote-details.items.quantity.title": "الكمية",
  "store/b2b-quotes.quote-details.items.price.title": "السعر",
  "store/b2b-quotes.quote-details.items.total.title": "المجموع",
  "store/b2b-quotes.quote-details.apply-discount.title": "تطبيق خصم النسبة المئوية",
  "store/b2b-quotes.quote-details.apply-discount.help-text": "قم بتطبيق نسبة مئوية للخصم على المجموع الفرعي لعرض الأسعار. لاحظ أن هذا سيؤدي إلى تجاوز أي تغييرات تم إجراؤها على أسعار وحدات SKU الفردية.",
  "store/b2b-quotes.quote-details.apply-discount.disabled-message": "لا يتوفر خصم النسبة المئوية لأن العرض الترويجي يطبق بالفعل خصمًا أعلى من الحد الأقصى للخصم اليدوي.",
  "store/b2b-quotes.quote-details.apply-discount.maxDiscount-text": "الخصومات اليدوية التي تزيد على {maxDiscount}% سيتم رفضها تلقائياً.",
  "store/b2b-quotes.quote-details.products.title": "المنتجات ({total})",
  "store/b2b-quotes.quote-details.notes-after-created.helpText": "يمكن إضافة الملاحظات بعد إنشاء الاقتباسات.",
  "store/b2b-quotes.quote-details.update-history.title": "سجل التحديث",
  "store/b2b-quotes.quote-details.update-history.update-details": "{index, plural, =0{تم إنشاءه}other{تم تحديثه}} يوم {date} من قبل {email} ({role}) مع الحالة {status}",
  "store/b2b-quotes.quote-details.update-history.notes": "ملاحظات:",
  "store/b2b-quotes.quote-details.add-note.label": "إضافة ملاحظة",
  "store/b2b-quotes.quote-details.update-success": "تم حفظ عرض الأسعار بنجاح",
  "store/b2b-quotes.quote-details.update-error": "حدث خطأ أثناء حفظ عرض الأسعار. راجع وحدة التحكم لمزيد من التفاصيل.",
  "store/b2b-quotes.quote-details.use-error": "حدث خطأ أثناء استخدام عرض الأسعار. راجع وحدة التحكم لمزيد من التفاصيل.",
  "store/b2b-quotes.quote-details.discount-error": "بعض الخصومات أعلى من المسموح به",
  "store/b2b-quotes.quote-details.percentage-discount.title": "نسبة الخصم",
  "store/b2b-quotes.quote-details.original-subtotal.title": "المجموع الفرعي الأصلي",
  "store/b2b-quotes.quote-details.original-price.title": "السعر الأصلي",
  "store/b2b-quotes.quote-details.quote-price.title": "السعر المقدّم",
  "store/b2b-quotes.quote-details.quoted-subtotal.title": "المجموع الفرعي المقدّم",
  "store/b2b-quotes.quote-details.expiration-date-change.title": "تغيير تاريخ انتهاء الصلاحية",
  "store/b2b-quotes.quote-locking.title": "عرض الأسعار قيد الاستخدام",
  "store/b2b-quotes.quote-locking.message": "تحتوي سلة التسوق الخاصة بك حاليًا على عناصر من عرض أسعار ولا يمكن تعديلها. يمكنك العودة إلى صفحة الدفع أو مسح عربة التسوق الخاصة بك لمتابعة التصفح.",
  "store/b2b-quotes.quote-locking.return-to-checkout": "الرجوع إلي الدفع",
  "store/b2b-quotes.quote-locking.clear-cart": "حذف عربة التسوق",
  "store/b2b-quotes.quote-status.ready": "جاهز",
  "store/b2b-quotes.quote-status.placed": "تم",
  "store/b2b-quotes.quote-status.revised": "تمت مراجعته",
  "store/b2b-quotes.quote-status.pending": "في الانتظار",
  "store/b2b-quotes.quote-status.expired": "منتهية",
  "store/b2b-quotes.quote-status.declined": "مرفوض"
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of b2b-quotes/admin/navigation.json from the b2b-quotes VTEX app.

[Assistant]
Here's the implementation:

```json
[
  {
    "section": "accountSettings",
    "path": "/admin/b2b-quotes/settings",
    "icon": "extensions-ic",
    "titleId": "admin/b2b-quotes.navigation.label",
    "searchKeyWordsHelpers": "admin/b2b-quotes.navigation.keywords"
  }
]

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of b2b-quotes/admin/routes.json from the b2b-quotes VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "admin.app.b2b-quotes": {
    "path": "/admin/app/b2b-quotes/*",
    "around": ["layout"]
  },
  "admin.app.b2b-quotes.settings": {
    "component": "AppSettings",
    "path": "/admin/app/b2b-quotes/settings"
  },
  "layout": {
    "component": "B2BQuotesAdminLayout"
  }
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of b2b-quotes/docs/README.md from the b2b-quotes VTEX app.

[Assistant]
Here's the implementation:

```markdown
📢 Use this project, [contribute](https://github.com/vtex-apps/b2b-quotes) to it or open issues to help evolve it using [Store Discussion](https://github.com/vtex-apps/store-discussion).

# B2B Quotes & Carts

[![All Contributors](https://img.shields.io/badge/all_contributors-0-orange.svg?style=flat-square)](#contributors-)

> ℹ The **B2B Quotes & Carts** app is part of VTEX’s [B2B Suite](https://developers.vtex.com/vtex-developer-docs/docs/vtex-b2b-suite) solution: a collection of apps that allow stores to manage organizations, storefront roles and permissions, and checkout settings for B2B commerce relationships. We recommend that you use it alongside the other apps in this suite for all functionalities to work as expected.

The B2B shopping experience frequently requires the possibility for customers to request quotes and negotiate prices with sales representatives.

The **B2B Quotes & Carts** app allows customers who are part of [B2B Organizations](https://developers.vtex.com/vtex-developer-docs/docs/vtex-b2b-organizations) to request a quote for a shopping cart and save it for future use – including its items, product quantities and prices. This enables price negotiations between them and the store’s sales representatives, as well as order approval flows within their organization.

## Before you start

First, make sure you have the [VTEX IO CLI (Command Line Interface)](https://developers.vtex.com/vtex-developer-docs/docs/vtex-io-documentation-vtex-io-cli-install) installed in your machine.

Note that installing **B2B Quotes & Carts** will also result in [Storefront Permissions](https://developers.vtex.com/vtex-developer-docs/docs/vtex-storefront-permissions) being installed as a dependency app – it allows you to grant specific storefront roles for B2B users in an organization. This is especially useful for organizations with multiple users who have different responsibilities, such as placing orders, approving orders or managing the organization users. See the [Storefront Permissions](https://developers.vtex.com/vtex-developer-docs/docs/vtex-storefront-permissions) app documentation for information on the available roles and how to customize their permissions (optional).

If you want to be able to manage roles and permissions on the VTEX Admin, you must install [Storefront Permissions UI](https://developers.vtex.com/vtex-developer-docs/docs/vtex-storefront-permissions-ui) as well.

Along with this app, we recommend installing [B2B Checkout Settings](https://developers.vtex.com/vtex-developer-docs/docs/vtex-b2b-checkout-settings), which includes quotes-related functionalities to checkout. For example, it allows you to add a `Create a Quote` button to the checkout’s shopping cart page, as well as “lock” the checkout if a quote is in use, preventing the cart contents from being changed until the order is placed.

## Installation

You can install the **B2B Quotes & Carts** app by running `vtex install vtex.b2b-quotes` in your terminal, using the [VTEX IO CLI](https://developers.vtex.com/vtex-developer-docs/docs/vtex-io-documentation-vtex-io-cli-installation-and-command-reference).

When you install the **B2B Quotes & Carts** app, a data entity named **quotes** is created in **Master Data V2**. This entity stores all the information related to quotes and saved carts generated through the app.

## How the app works

The **B2B Quotes & Carts** app enables a shared [My Quotes](#my-quotes) page for members of a B2B organization, from where it is possible to create a quote or saved cart and view existing quotes and saved carts.

It is also possible to manage each quote or saved cart’s [details](#quote-details), including the possibility to edit them, request adjustments, approve or decline them or use them to place an order.

In addition, the app enables a set of [email templates](#email-templates) to notify members of the organization of quote status changes through Message Center.

The ability to use these resources depends on having the required [storefront permissions](#storefront-permissions), as explained in the next section.

| **Permission**                                | **Default roles**                                                                                |
| --------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
| **Create Quotes and Carts**                   | Store Admin
Sales Admin
Sales Manager
Sales Representative
Organization Admin
Organization Approver
Organization Buyer |
| **Access My Cost Center's Quotes and Carts**  | Sales Representative
Organization Approver
Organization Buyer                                                                      |
| **Access My Organization's Quotes and Carts** | Sales Manager
Organization Admin                                                                                |
| **Access All Quotes and Carts**               | Store Admin
Sales Admin                                                                                |
| **Place Orders from Quotes and Carts**        | Store Admin
Sales Admin
Sales Manager
Sales Representative
Organization Admin
Organization Approver                       |
| **Decline Quotes and Carts**                  | Store Admin
Sales Admin
Sales Manager
Sales Representative
Organization Admin
Organization Approver                       |
| **Edit My Cost Center's Quotes and Carts**    | Sales Representative                                                                                |
| **Edit My Organization's Quotes and Carts**   | Sales Manager                                                                                |
| **Edit All Quotes and Carts**                 | Store Admin
Sales Admin                                                                                |

If you want to be able to manage roles and permissions for **B2B Quotes & Carts** on the VTEX Admin, you must install [Storefront Permissions UI](https://developers.vtex.com/vtex-developer-docs/docs/vtex-storefront-permissions-ui) and refer to its documentation.

### My Quotes

The **B2B Quotes & Carts** app enables the **My Quotes** page, an interface to manage quotes and saved carts in the storefront, provided that the user is logged in and has the required storefront permissions.

To access **My Quotes**, customers or sales users must follow the instructions below.

1. Access the storefront and log in.
2. Click `Hello, {name}`.
3. Click on **My Account**.
4. Go to **Quotes and Saved Carts** on the sidebar. You will see the page below.

![my-quotes](https://user-images.githubusercontent.com/77292838/161637102-66a15213-e70d-421a-9e90-5a68ddc25825.png)

> ℹ Quotes and saved carts are shown together in the same table. The differences are the following:
> _ Only quotes can have a status of **Pending** or **Revised**.
> _ Sales users are not notified when saved carts are created. \* It is possible to edit a saved cart such that it becomes a quote, as explained in [Quote Details](#quote-details).

In this page, it is possible to use the following resources:

- **Search bar**: allows you to look up specific quotes or saved carts by name.
- **Filters**: allows you to filter the displayed quotes or saved carts by _Status_, _Organization \_or \_Cost Center._ The two last options are available under `More`.
- **New quote**: allows you to create a quote or saved cart by clicking on the `New Quote` button. See more details on this in the [Creating a quote or saved cart](#creating-a-quote-or-saved-cart) section.

The list of quotes and saved carts displays the following information:

- **Ref. Name:** the quote or saved cart’s reference name.
- **Subtotal:** subtotal of the shopping cart associated with the quote or saved cart.
- **Created by:** email of the user who created the quote or saved cart.
- **Created on:** date when the quote or saved cart was created, in the MM/DD/YYYY format.
- **Expiration:** date when the quote or saved cart will expire, in the MM/DD/YYYY format.
- **Status:** current state of the quote or saved cart, including the following options:
  - **Ready**: the quote or saved cart is ready to be used to place an order.
  - **Pending**: the quote is waiting for review and adjustment by a sales user.
  - **Revised**: the quote is waiting for additional review and adjustment by a sales user.
  - **Declined**: the quote or saved cart has been declined and cannot be used to place an order.
  - **Expired**: the quote or saved cart has reached its expiration date and cannot be used to place an order.
  - **Placed**: the quote or saved cart has been used to place an order and cannot be used again.
- **Last Update:** date when the quote or saved cart was updated for the last time, in the MM/DD/YYYY format.
- **Organization:** name of the organization the quote or saved cart is associated with.
- **Cost Center:** name of the cost center the quote or saved cart is associated with.

From the list, users may click on any row or click on  **> Details** to view and edit the [details](#quote-details) of that quote or saved cart, according to what their permissions allow.

Depending on the current user’s [storefront permissions](#storefront-permissions), the **My Quotes** page will show a list of quotes and saved carts containing one of the following:

| **Permission**                                | **Content**                                                                     |
| --------------------------------------------- | ------------------------------------------------------------------------------- |
| **Access My Cost Center's Quotes and Carts**  | All quotes and saved carts created by users in the current user’s cost center.  |
| **Access My Organization's Quotes and Carts** | All quotes and saved carts created by users in the current user’s organization. |
| **Access All Quotes and Carts**               | All quotes and saved carts in the current store, regardless of organization.    |

### Creating a quote or saved cart

In order to create a quote or saved cart, it is necessary to have the **Create Quotes and Carts** [permission](#storefront-permissions). After adding products to the cart in the storefront, users with these permissions can use the contents of their cart to create a saved cart or to request a quote. They should follow the instructions below.

1. Fill in the shopping cart with the desired products.
2. Take one of the following actions to create a quote or saved cart:

   - Clicking the `Create a Quote` button during checkout, provided that **B2B Checkout Settings** is installed and this button is enabled in the app’s [configuration](https://developers.vtex.com/vtex-developer-docs/docs/vtex-b2b-checkout-settings#configuration).
   - Navigating to the `/b2b-quotes/create` route.
   - Clicking the `+ New` button on the **My Quotes** page.

     Any of these paths will lead to the **Create Quote** page, illustrated below.

3. Fill in the quote or saved cart’s **Name**.
4. Make sure that all the information in the **Create Quote** page is correct:
   - **Original Subtotal:** subtotal of the shopping cart before any discounts.
   - **Percentage Discount:** percentage of the discount offered by a sales representative.
   - **Quoted Subtotal:** subtotal of the quote, including any discounts offered by sales representatives.
   - **Image:** image of the selected product.
   - **Ref. Code:** reference code of the selected product.
   - **Name:** name of the product.
   - **Original Price:** original price of the product.
   - **Quoted Price:** price of the product including any discounts offered by sales representatives.
   - **Quantity:** quantity of items.
   - **Total:** total value considering the quoted price and the quantity of items.
5. Write a note in the optional **Add Note** field, if necessary.

   Notes added here will be visible as part of the quote or saved cart’s [Update History](#quote-details) and can be seen by salespeople or other members of your organization and cost center. For example, if a user wishes to request a specific discount from the B2B store’s salesperson, they may include this request as a note.

6. Click on one of the following options:

   - `Save for later`: clicking on this button will result in the creation of a saved cart. The status of the newly created saved cart will be set as **Ready**, meaning that it can immediately be used to place an order by any user within that organization or cost center who has the **Place Orders from Quotes and Carts** [permission](#storefront-permissions).
   - `Request quote`: clicking on this button will result in the creation of a quote. The status of the newly created quote will be set as **Pending**, meaning that it will need to be reviewed and adjusted by a salesperson before any special discounts can be applied.

     Note that a **Pending** quote can still be used to place an order, as it is essentially a saved cart at this point, with the original item quantities and prices from the user’s shopping cart.

The items in the table, along with the quantities and prices shown, will be saved when the quote or saved cart is created.

![create-quote](https://user-images.githubusercontent.com/77292838/161637106-44d41347-2ee4-4191-bfe9-8dc7e67c783e.png)

### Quote Details

The **Quote Details** page displays all the information about a specific quote or saved cart, as well as the available actions, depending on its status and the user’s [storefront permissions](#storefront-permissions).

Users can access this page through the storefront by going to **My Account > Quotes and Saved Carts**.

![quote-details](https://user-images.githubusercontent.com/77292838/161637111-043851a7-9215-49eb-81d3-42f5a8b3936c.png)

This page displays the following information about a specific quote:

- **Name:** reference name of the quote or saved cart.
- **Original Subtotal:** subtotal of the shopping cart before any discounts.
- **Percentage Discount:** percentage of the discount offered by a sales representative.
- **Quoted Subtotal:** subtotal of the quote, including any discounts offered by sales representatives.
- **Expiration:** date when the quote or saved cart will expire, in the MM/DD/YYYY format.
- **Status:** current state of the quote or saved cart, as in [My Quotes](#my-quotes).
- **Image:** image of the selected product.
- **Ref. Code:** reference code of the selected product.
- **Name:** name of the product.
- **Original Price:** original price of the product.
- **Quoted Price:** price of the product including any discounts offered by sales representatives.
- **Quantity:** quantity of items.
- **Total:** total value considering the quoted price and the quantity of items.
- **Update History:** includes a history of all events related to the quote, such as its creation and discounts offered, including notes if there are any.
- **Add Note:** optional field to write comments or notes. Notes added here will be visible as part of the quote or saved cart’s [Update History](#update-history). For example, if a user wishes to request a specific discount from their salesperson, they may include this request as a note.

The following actions are available, depending on the user’s storefront permissions:

| Actions                                                                                | Required storefront permissions                                                                                |
| ----------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
| [Editing a quote](#editing-a-quote)                                                             | Any of the following:
Edit My Cost Center's Quotes and Carts
Edit My Organization's Quotes and Carts
Edit All Quotes and Carts       |
| [Requesting additional adjustments to a quote](#requesting-additional-adjustments-to-a-quote)   | Any of the following:
Access My Cost Center's Quotes and Carts
Access My Organization's Quotes and Carts
Access All Quotes and Carts |
| [Declining a quote or saved cart](#declining-a-quote-or-saved-cart)                             | Decline Quotes and Carts                                                                                |
| [Using a quote or saved cart to place an order](#using-a-quote-or-saved-cart-to-place-an-order) | Place Orders from Quotes and Carts                                                                                |

#### Editing a quote

Users with **Edit** [permissions](#storefront-permissions) – who are typically users associated with any of the Sales roles or the Store Admin role – can edit quotes in the **Quote Details** page as follows:

- Change the price or quantity of each SKU by editing them directly in the **Quoted Price** and **Quantity** columns of the product list within the quote.
- **Apply Percentage Discount:** apply a percentage discount to the entire quote by dragging the discount bar up to the desired percentage. This will override any changes made to individual product prices.
- **Expiration Date Change:** change the quote’s expiration date.
- **Add Note:** write an optional comment.

After making any of the above edits on the quote details page, the user must save the quote by clicking `Save quote`.

This will have the effect of setting the quote’s status to **Ready** and notifying any user who has previously interacted with the quote of the change via email – check [Email templates](#email-templates) for more details on this notification.

![quote-details-edit](https://user-images.githubusercontent.com/77292838/161637108-aeaeef58-296f-407a-9c00-b5ffe9d509fb.png)

> ℹ To take discounts into consideration, the **B2B Quotes and Carts** app is integrated with the [Order Authorization](https://help.vtex.com/en/tutorial/how-order-authorization-works--3MBK6CmKHAuUjMBieDU0pn) system. If there is a manual discount rule that will automatically deny a discount above a certain percent, **B2B Quotes and Carts** will not allow a discount above this amount to be applied.

#### Requesting additional adjustments to a quote

Users who lack any of the **Edit** permissions but have any of the **Access** [permissions](#storefront-permissions) can add additional notes to a quote or saved cart if its current status is **Ready**, **Pending**, or **Revised**.

If a quote is **Ready**, meaning that it has been adjusted and saved by a sales user, its status will change to **Revised** when an organization user adds notes and clicks `Submit to Sales Rep`. This is intended as a way for the organization user to request additional adjustments to pricing or quantity. All users who have previously interacted with the quote will receive a notification including the contents of the notes field – check [Email templates](#email-templates) for more details on this notification.

#### Declining a quote or saved cart

A declined quote or saved cart can no longer be edited or used to place an order. Users with the **Decline Quotes and Carts** [permission](#storefront-permissions) may decline a quote or saved cart if its current status is **Ready**, **Pending** or **Revised**. This can be done by clicking the `Decline` button on the quote details page.

All users who have previously interacted with the quote will be notified that it has been declined – check [Email templates](#email-templates) for more details on this notification.

#### Using a quote or saved cart to place an order

Users with the **Place Orders from Quotes and Carts** [permission](#storefront-permissions) may use a quote or saved cart to place an order if its current status is **Ready**, **Pending**, or **Revised**.

They can do this by clicking the `Use Quote` button on the quote details page. The user will be redirected to checkout with the contents of the quote or saved cart added to their current cart. Anything that was previously in the user’s cart will be cleared.

If the [B2B Checkout Settings](https://developers.vtex.com/vtex-developer-docs/docs/vtex-b2b-checkout-settings) app is installed, the checkout will be in a “locked” state until the order is placed, meaning that product quantities cannot be changed, and products cannot be added or removed. If there are not enough items in stock to meet the quantity specified in the quote or saved cart, the amount will be automatically adjusted. If a given product has no availability, checkout will allow it to be removed from the cart.

Once the order has been placed, the quote or saved cart’s status will automatically change to **Placed**, after which point it cannot be used again.

### Email templates

The **B2B Quotes & Carts** app provides a set of three email templates to be sent to B2B users, triggered automatically based on specific changes:

| **Template name**  | **Recipients**  | **Email trigger**  |
|---------------------|----------------|--------------------|
| Quote created  | Only users with the **sales-admin** role receive this email. | Sent only if all of the following conditions are met:
• The quote creator does not have permission to edit quotes.
• The quote must be created using the `Request quote` button and cannot be saved for later. |
| Quote placed  | All users who edited, requested adjustments or approved the quote. | Sent when an order is placed from the existing quote. |
| Quote updated  | All users who interacted with the quote. | Sent when the quote is updated in any way, including changes in discounts, updates to expiration dates, addition of new notes, or quote refusal. |

If you want to view or edit any of these templates, follow the steps below.

1. In the VTEX Admin, go to **Customer > Message center > Templates**.
2. In the search bar, type `quote`.
3. Click on the template you want to view or edit.
4. Make the desired changes in the template. You can learn more about editing **Message Center** templates by reading our documentation on [How to create and edit transactional email templates](https://help.vtex.com/en/tracks/transactional-emails--6IkJwttMw5T84mlY9RifRP/335JZKUYgvYlGOJgvJYxRO).
5. Click on `Save`.

## **Configuration (optional)**

To change the app’s settings, access **Account Settings** > **B2B Quotes** in the VTEX Admin.

On this page, you can configure the following option:

- **Default expiration period (in days) for quotes and saved carts:** this field allows you to configure the default expiration date for new quotes requested by customers in your store. The default value is 30 days. The minimum value you can set is 1, and there is no maximum value.

Make sure you click on `Save Settings` after any changes.

Note that users with [storefront permissions](#storefront-permissions) to edit quotes will be able to adjust the expiration dates of individual quotes as needed.

![app-configuration](https://user-images.githubusercontent.com/6306265/163218059-903cfbcf-0077-45d2-9457-19ee0ba4d5c8.png)

## Contributors ✨

Thanks goes to these wonderful people:

This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind are welcome!

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of b2b-quotes/vtex.storefront-permissions/configuration.json from the b2b-quotes VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "name": "B2B Quotes & Carts",
  "features": [
    {
      "label": "Create Quotes and Carts",
      "value": "create-quotes",
      "roles": [
        "store-admin",
        "sales-admin",
        "sales-manager",
        "sales-representative",
        "customer-admin",
        "customer-approver",
        "customer-buyer"
      ]
    },
    {
      "label": "Access My Cost Center's Quotes and Carts",
      "value": "access-quotes-costcenter",
      "roles": ["customer-approver", "customer-buyer", "sales-representative"]
    },
    {
      "label": "Access My Organization's Quotes and Carts",
      "value": "access-quotes-organization",
      "roles": ["customer-admin", "sales-manager"]
    },
    {
      "label": "Access All Sales Channels' Quotes and Carts",
      "value": "access-quotes-all-saleschannel",
      "roles": [
        "store-admin",
        "sales-admin",
        "sales-manager",
        "sales-representative",
        "customer-admin",
        "customer-approver",
        "customer-buyer"
      ]
    },
    {
      "label": "Access All Quotes and Carts",
      "value": "access-quotes-all",
      "roles": ["store-admin", "sales-admin"]
    },
    {
      "label": "Place Orders from Quotes and Carts",
      "value": "use-quotes",
      "roles": [
        "store-admin",
        "customer-admin",
        "customer-approver",
        "sales-admin",
        "sales-manager",
        "sales-representative"
      ]
    },
    {
      "label": "Decline Quotes and Carts",
      "value": "decline-quotes",
      "roles": [
        "store-admin",
        "sales-admin",
        "sales-manager",
        "sales-representative",
        "customer-admin",
        "customer-approver"
      ]
    },
    {
      "label": "Edit My Cost Center's Quotes and Carts",
      "value": "edit-quotes-costcenter",
      "roles": ["sales-representative"]
    },
    {
      "label": "Edit My Organization's Quotes and Carts",
      "value": "edit-quotes-organization",
      "roles": ["sales-manager"]
    },
    {
      "label": "Edit All Quotes and Carts",
      "value": "edit-quotes-all",
      "roles": ["store-admin", "sales-admin"]
    }
  ]
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of b2b-quotes/react/B2BQuotesAdminLayout.tsx from the b2b-quotes VTEX app.

[Assistant]
Here's the implementation:

```tsx
import type { FunctionComponent } from 'react'
import React from 'react'
import { createSystem, ToastProvider } from '@vtex/admin-ui'

const B2BQuotesAdminLayout: FunctionComponent = ({ children }) => {
  const [SystemProvider] = createSystem()

  return (
    <SystemProvider>
      <ToastProvider>{children}</ToastProvider>
    </SystemProvider>
  )
}

export default B2BQuotesAdminLayout

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of b2b-quotes/react/QuotesTable.tsx from the b2b-quotes VTEX app.

[Assistant]
Here's the implementation:

```tsx
import QuotesTableContainer from './components/QuotesTableContainer'

export default QuotesTableContainer

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of b2b-quotes/react/AppSettings.tsx from the b2b-quotes VTEX app.

[Assistant]
Here's the implementation:

```tsx
import AppSettings from './components/AppSettings'

export default AppSettings

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of b2b-quotes/react/package.json from the b2b-quotes VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "name": "vtex.b2b-quotes",
  "version": "3.0.3",
  "scripts": {
    "test": "vtex-test-tools test"
  },
  "dependencies": {
    "@vtex/admin-ui": "^0.132.7",
    "@vtex/css-handles": "^1.0.0",
    "graphql": "^14.5.8",
    "react": "^16.9.2",
    "react-apollo": "^3.1.5",
    "react-intl": "^5.13.4",
    "axios": "1.4.0"
  },
  "devDependencies": {
    "@types/jest": "^24.0.18",
    "@types/node": "^12.7.5",
    "@types/react": "^16.9.2",
    "@types/react-intl": "^3.0.0",
    "@vtex/test-tools": "^3.4.3",
    "@vtex/tsconfig": "^0.5.6",
    "vtex.b2b-organizations-graphql": "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.b2b-organizations-graphql@0.10.0/public/@types/vtex.b2b-organizations-graphql",
    "vtex.b2b-quotes-graphql": "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.b2b-quotes-graphql@1.3.0/public/@types/vtex.b2b-quotes-graphql",
    "vtex.checkout-graphql": "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.checkout-graphql@0.65.1/public/@types/vtex.checkout-graphql",
    "vtex.checkout-resources": "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.checkout-resources@0.48.0/public/@types/vtex.checkout-resources",
    "vtex.css-handles": "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.css-handles@0.4.4/public/@types/vtex.css-handles",
    "vtex.format-currency": "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.format-currency@0.3.0/public/@types/vtex.format-currency",
    "vtex.my-account": "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.my-account@1.25.0/public/@types/vtex.my-account",
    "vtex.my-account-commons": "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.my-account-commons@1.6.0/public/@types/vtex.my-account-commons",
    "vtex.order-auth-graphql": "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.order-auth-graphql@0.2.1/public/_types/react",
    "vtex.order-manager": "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.order-manager@0.11.1/public/@types/vtex.order-manager",
    "vtex.render-runtime": "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.render-runtime@8.132.3/public/@types/vtex.render-runtime",
    "vtex.store": "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.store@2.123.0/public/@types/vtex.store",
    "vtex.store-graphql": "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.store-graphql@2.149.3/public/@types/vtex.store-graphql",
    "vtex.storefront-permissions": "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.storefront-permissions@1.14.0/public/@types/vtex.storefront-permissions",
    "vtex.styleguide": "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.styleguide@9.146.1/public/@types/vtex.styleguide"
  }
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of b2b-quotes/react/B2BQuotesLink.tsx from the b2b-quotes VTEX app.

[Assistant]
Here's the implementation:

```tsx
import type { FunctionComponent, ReactElement } from 'react'
import { useEffect, useState } from 'react'
import { defineMessages, useIntl } from 'react-intl'
import { useQuery } from 'react-apollo'

import { useSessionResponse } from './utils/helpers'
import CHECK_PERMISSIONS from './graphql/checkPermissions.graphql'

const messages = defineMessages({
  myQuotes: {
    id: 'store/b2b-quotes.myAccountLink',
  },
})

const B2BQuotesLink: FunctionComponent<Props> = ({ render }) => {
  const { formatMessage } = useIntl()
  const sessionResponse: any = useSessionResponse()
  const userEmail = sessionResponse?.namespaces?.profile?.email?.value

  const [show, setShow] = useState(false)

  const { data } = useQuery(CHECK_PERMISSIONS, {
    variables: { email: userEmail },
    skip: !userEmail,
  })

  useEffect(() => {
    if (!data) {
      return
    }

    if (data.getQuoteEnabledForUser) {
      setShow(true)
    }
  }, [data, userEmail, sessionResponse])

  const parameter = {
    name: formatMessage(messages.myQuotes),
    path: `/b2b-quotes`,
  }

  return show ? render([parameter]) : null
}

type Props = {
  render: (links: Array<{ name: string; path: string }>) => ReactElement
  intl: any
}

export default B2BQuotesLink

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of b2b-quotes/react/QuoteDetails.tsx from the b2b-quotes VTEX app.

[Assistant]
Here's the implementation:

```tsx
import QuoteDetails from './components/QuoteDetails/QuoteDetails'

export default QuoteDetails

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of b2b-quotes/react/print.css from the b2b-quotes VTEX app.

[Assistant]
Here's the implementation:

```css
@media print {
  div[class*='vtex-store-header'],
  div[class*='vtex-store-footer'],
  div[class*='noPrinting'] {
    display: none !important;
  }
  div[class*='printing-area'] {
    margin: 100px 20px;
  }
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of b2b-quotes/react/B2BQuotesListAccount.tsx from the b2b-quotes VTEX app.

[Assistant]
Here's the implementation:

```tsx
import type { FC } from 'react'
import React, { Fragment } from 'react'
import { Route } from 'vtex.my-account-commons/Router'
import { useRuntime } from 'vtex.render-runtime'
import { Spinner } from 'vtex.styleguide'

const B2BQuotesRedirect: FC = () => {
  const { navigate } = useRuntime()

  navigate({
    page: 'store.b2b-quotes',
  })

  return <Spinner />
}

const B2BQuotesListAccount = () => (
  <Fragment>
    <Route exact path="/b2b-quotes" component={B2BQuotesRedirect} />
  </Fragment>
)

export default B2BQuotesListAccount

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of b2b-quotes/react/B2BQuotesLockingModal.tsx from the b2b-quotes VTEX app.

[Assistant]
Here's the implementation:

```tsx
import B2BQuotesLockingModal from './components/B2BQuotesLockingModal'

export default B2BQuotesLockingModal

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of b2b-quotes/store/blocks.json from the b2b-quotes VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "store.b2b-quotes": {
    "blocks": ["quotes-table"]
  },
  "store.create-b2b-quote": {
    "blocks": ["quote-details"]
  },
  "store.b2b-quote-details": {
    "blocks": ["quote-details"]
  }
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of b2b-quotes/store/interfaces.json from the b2b-quotes VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "store.b2b-quotes": {
    "required": ["quotes-table"]
  },
  "quotes-table": {
    "component": "QuotesTable"
  },
  "quotes-locking-modal": {
    "component": "B2BQuotesLockingModal"
  },
  "store.create-b2b-quote": {
    "required": ["quote-details"]
  },
  "store.b2b-quote-details": {
    "required": ["quote-details"]
  },
  "quote-details": {
    "component": "QuoteDetails"
  },
  "my-account-link.b2b-quotes": {
    "component": "B2BQuotesLink"
  },
  "my-account-page.b2b-quotes": {
    "component": "B2BQuotesListAccount"
  }
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of b2b-quotes/store/routes.json from the b2b-quotes VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "store.b2b-quotes": {
    "title": "My Quotes and Carts",
    "path": "/b2b-quotes"
  },
  "store.create-b2b-quote": {
    "title": "Create Quote",
    "path": "/b2b-quotes/create"
  },
  "store.b2b-quote-details": {
    "title": "Quote Details",
    "path": "/b2b-quotes/:id"
  }
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of b2b-quotes/store/plugins.json from the b2b-quotes VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "my-account-pages > my-account-page": "my-account-page.b2b-quotes",
  "my-account-menu > my-account-link": "my-account-link.b2b-quotes"
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of b2b-quotes/react/typings/storefront.d.ts from the b2b-quotes VTEX app.

[Assistant]
Here's the implementation:

```typescript
import type { FunctionComponent, Component } from 'react'

declare global {
  interface StorefrontFunctionComponent<P = unknown>
    extends FunctionComponent<P> {
    getSchema?(props: P): Record<string, unknown>
    schema?: Record<string, unknown>
  }

  interface StorefrontComponent<P = unknown, S = unknown>
    extends Component<P, S> {
    getSchema?(props: P): Record<string, unknown>
    schema: Record<string, unknown>
  }
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of b2b-quotes/react/typings/global.d.ts from the b2b-quotes VTEX app.

[Assistant]
Here's the implementation:

```typescript
import type { OrderForm as OrderFormType } from 'vtex.checkout-graphql'

declare global {
  interface QuoteSimple {
    id: string
    referenceName: string
    creatorEmail: string
    creatorRole: string
    creationDate: string
    expirationDate: string
    lastUpdate: string
    subtotal: number
    status: string
    organization: string
    organizationName: string
    costCenter: string
    costCenterName: string
    viewedBySales: boolean
    viewedByCustomer: boolean
    hasChildren?: boolean | null
    childrenQuantity?: number | null
    parentQuote?: string | null
    seller?: string
    sellerName?: string
    rowLoading?: boolean
  }

  interface Quote extends QuoteSimple {
    items: QuoteItem[]
    updateHistory: QuoteUpdate[]
  }

  interface QuoteUpdate {
    email: string
    role: string
    date: string
    status: string
    note: string
  }

  interface QuoteItem {
    name: string
    skuName: string
    refId: string
    id: string
    productId: string
    imageUrl: string
    listPrice: number
    price: number
    quantity: number
    sellingPrice: number
    seller: string
    error: boolean | undefined
  }

  interface FilterStatement {
    subject: string
    verb: string
    object: Record<string, unknown> | string
  }

  interface MessageDescriptor {
    id: string
    description?: string | Record<string, unknown>
    defaultMessage?: string
    values?: Record<string, unknown>
  }

  interface OrderFormContext {
    loading: boolean
    orderForm: OrderFormType | undefined
    setOrderForm: (orderForm: Partial<OrderFormType>) => void
  }

  interface Seller {
    id: string
    name: string
  }
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of b2b-quotes/react/typings/css.d.ts from the b2b-quotes VTEX app.

[Assistant]
Here's the implementation:

```typescript
declare module '*.css' {
  const css: any
  export default css
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of b2b-quotes/react/typings/graphql.d.ts from the b2b-quotes VTEX app.

[Assistant]
Here's the implementation:

```typescript
declare module '*.graphql' {
  import type { DocumentNode } from 'graphql'

  const value: DocumentNode
  export default value
}

declare module '*.gql' {
  import type { DocumentNode } from 'graphql'

  const value: DocumentNode
  export default value
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of b2b-quotes/react/typings/vtex.styleguide.d.ts from the b2b-quotes VTEX app.

[Assistant]
Here's the implementation:

```typescript
declare module 'vtex.styleguide' {
  export const PageHeader
  export const Alert
  export const Modal
  export const DatePicker
  export const Box
  export const Card
  export const Checkbox
  export const Dropdown
  export const Spinner
  export const Table
  export const Button
  export const ButtonPlain
  export const IconWarning
  export const IconCheck
  export const Input
  export const InputButton
  export const InputCurrency
  export const InputSearch
  export const IconCaretRight
  export const IconCaretDown
  export const IconCaretUp
  export const IconArrowDown
  export const IconArrowBack
  export const IconArrowUp
  export const IconClose
  export const IconExternalLink
  export const IconSearch
  export const IconSuccess
  export const IconCopy
  export const IconCog
  export const Tooltip
  export const Totalizer
  export const ToastConsumer
  export const ToastProvider
  export const ToastContext
  export const withToast
  export const ButtonWithIcon
  export const Pagination
  export const Collapsible
  export const Textarea
  export const NumericStepper
  export const EmptyState
  export const Tab
  export const Tag
  export const Toggle
  export const Layout
  export const Tabs
  export const Divider
  export const ModalDialog
  export const PageBlock
  export const Slider
  export const AutocompleteInput
  // eslint-disable-next-line @typescript-eslint/naming-convention
  export const EXPERIMENTAL_Modal
  export const RadioGroup
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of b2b-quotes/react/utils/shallowEquals.ts from the b2b-quotes VTEX app.

[Assistant]
Here's the implementation:

```typescript
export function shallowEqual(
  object1: Record<string, unknown>,
  object2: Record<string, unknown>
) {
  const keys1 = Object.keys(object1)
  const keys2 = Object.keys(object2)

  if (keys1.length !== keys2.length) {
    return false
  }

  for (const key of keys1) {
    if (object1[key] !== object2[key]) {
      return false
    }
  }

  return true
}

export function arrayShallowEqual(
  array1: Array<Record<string, any>>,
  array2: Array<Record<string, any>>
) {
  let equal = true

  for (const [index, member] of array1.entries()) {
    if (!shallowEqual(member, array2[index])) {
      equal = false
      break
    }
  }

  return equal
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of b2b-quotes/react/utils/status.ts from the b2b-quotes VTEX app.

[Assistant]
Here's the implementation:

```typescript
export const enum Status {
  EXPIRED = 'expired',
  PLACED = 'placed',
  DECLINED = 'declined',
  READY = 'ready',
  REVISED = 'revised',
  PENDING = 'pending',
}

export const LabelByStatusMap: Record<string, string> = {
  [Status.READY]: 'success',
  [Status.PLACED]: 'neutral',
  [Status.DECLINED]: 'error',
  [Status.EXPIRED]: 'error',
  [Status.PENDING]: 'warning',
  [Status.REVISED]: 'warning',
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of b2b-quotes/react/utils/helpers.ts from the b2b-quotes VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { useEffect, useState } from 'react'

import { getSession } from '../modules/session'
import { Status } from './status'

export const initQuoteFromOrderForm = (orderForm: any) => {
  const quoteItems = [] as QuoteItem[]

  orderForm?.items?.forEach((item: QuoteItem) => {
    const existingItem = quoteItems.find(
      (existing: QuoteItem) =>
        existing.id === item.id && existing.sellingPrice === item.sellingPrice
    )

    if (existingItem) {
      existingItem.quantity += item.quantity
    } else if (item.sellingPrice !== 0) {
      quoteItems.push({
        ...item,
        listPrice: item.listPrice * 100,
        price: item.price * 100,
        sellingPrice: item.sellingPrice * 100,
      })
    }
  })

  return quoteItems
}

export const itemDiscountEligible = ({
  listPrice,
  sellingPrice,
  error,
  maxDiscountState,
}: {
  listPrice: number
  sellingPrice: number
  error: boolean | undefined
  maxDiscountState: number
}) => {
  return !(
    !error &&
    Math.round(100 - (sellingPrice / listPrice) * 100) > maxDiscountState
  )
}

export const useSessionResponse = () => {
  const [session, setSession] = useState<unknown>()
  const sessionPromise = getSession()

  useEffect(() => {
    if (!sessionPromise) {
      return
    }

    sessionPromise.then((sessionResponse) => {
      const { response } = sessionResponse

      setSession(response)
    })
  }, [sessionPromise])

  return session
}

export const getEmptySimpleQuote = (parentQuote: string): QuoteSimple => ({
  id: '',
  referenceName: '',
  subtotal: 0,
  costCenter: '',
  costCenterName: '',
  creatorEmail: '',
  creatorRole: '',
  creationDate: '',
  expirationDate: '',
  lastUpdate: '',
  organization: '',
  organizationName: '',
  status: '',
  viewedByCustomer: true,
  viewedBySales: true,
  parentQuote,
  rowLoading: true,
})

export const isQuoteUsable = (permissions: string[], status: string) =>
  permissions.includes('use-quotes') &&
  status &&
  status !== Status.EXPIRED &&
  status !== Status.PLACED &&
  status !== Status.DECLINED

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of b2b-quotes/react/utils/messages.ts from the b2b-quotes VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { defineMessages } from 'react-intl'

const storePrefix = 'store/b2b-quotes.'
const adminPrefix = 'admin/b2b-quotes.'

export const quoteMessages = defineMessages({
  updateSuccess: {
    id: `${storePrefix}quote-details.update-success`,
  },
  updateError: {
    id: `${storePrefix}quote-details.update-error`,
  },
  useQuote: {
    id: `${storePrefix}quote-details.use-quote`,
  },
  useError: {
    id: `${storePrefix}quote-details.use-error`,
  },
  updatePageTitle: {
    id: `${storePrefix}quote-details.title`,
  },
  back: {
    id: `${storePrefix}back`,
  },
  subtotal: {
    id: `${storePrefix}quote-details.subtotal.title`,
  },
  expiration: {
    id: `${storePrefix}quote-details.expiration.title`,
  },
  status: {
    id: `${storePrefix}quote-details.status.title`,
  },
  image: {
    id: `${storePrefix}quote-details.items.image.title`,
  },
  refCode: {
    id: `${storePrefix}quote-details.items.refId.title`,
  },
  name: {
    id: `${storePrefix}quote-details.items.name.title`,
  },
  price: {
    id: `${storePrefix}quote-details.items.price.title`,
  },
  quantity: {
    id: `${storePrefix}quote-details.items.quantity.title`,
  },
  total: {
    id: `${storePrefix}quote-details.items.total.title`,
  },
  addNote: {
    id: `${storePrefix}quote-details.add-note.label`,
  },
  percentageDiscount: {
    id: `${storePrefix}quote-details.percentage-discount.title`,
  },
  quotedSubtotal: {
    id: `${storePrefix}quote-details.quoted-subtotal.title`,
  },
  originalSubtotal: {
    id: `${storePrefix}quote-details.original-subtotal.title`,
  },
  quotePrice: {
    id: `${storePrefix}quote-details.quote-price.title`,
  },
  originalPrice: {
    id: `${storePrefix}quote-details.original-price.title`,
  },
  expirationDateChange: {
    id: `${storePrefix}quote-details.expiration-date-change.title`,
  },
  productsTitle: {
    id: `${storePrefix}quote-details.products.title`,
  },
  historyTitle: {
    id: `${storePrefix}quote-details.update-history.title`,
  },
  notesAfterCreatedHelpText: {
    id: `${storePrefix}quote-details.notes-after-created.helpText`,
  },
  createPageTitle: {
    id: `${storePrefix}create.title`,
  },
  createSuccess: {
    id: `${storePrefix}create.success`,
  },
  createError: {
    id: `${storePrefix}create.error`,
  },
  required: {
    id: `${storePrefix}create.required`,
  },
  placeholderName: {
    id: `${storePrefix}create.placeholder.quotationName`,
  },
  labelName: {
    id: `${storePrefix}create.nameLabel`,
  },
  emptyState: {
    id: `${storePrefix}create.empty-state-label`,
  },
})

export const tableMessages = defineMessages({
  details: {
    id: `${storePrefix}quotes-table.details.label`,
  },
  refName: {
    id: `${storePrefix}quotes-table.referenceName.title`,
  },
  subtotal: {
    id: `${storePrefix}quotes-table.subtotal.title`,
  },
  email: {
    id: `${storePrefix}quotes-table.creatorEmail.title`,
  },
  creationDate: {
    id: `${storePrefix}quotes-table.creationDate.title`,
  },
  expirationDate: {
    id: `${storePrefix}quotes-table.expirationDate.title`,
  },
  status: {
    id: `${storePrefix}quotes-table.status.title`,
  },
  lastUpdate: {
    id: `${storePrefix}quotes-table.lastUpdate.title`,
  },
  organization: {
    id: `${storePrefix}quotes-table.organization.title`,
  },
  costCenter: {
    id: `${storePrefix}quotes-table.costCenter.title`,
  },
  organizationAndCostCenter: {
    id: `${storePrefix}quotes-table.orgAndCostCenter.title`,
  },
  searchOrganization: {
    id: `${storePrefix}quotes-table.searchOrganization.title`,
  },
  searchCostCenter: {
    id: `${storePrefix}quotes-table.searchCostCenter.title`,
  },
  any: {
    id: `${storePrefix}quotes-table.filters.any`,
  },
  is: {
    id: `${storePrefix}quotes-table.filters.is`,
  },
  emptyState: {
    id: `${storePrefix}quotes-table.empty-state-label`,
  },
  showRows: {
    id: `${storePrefix}quotes-table.showRows`,
  },
  of: {
    id: `${storePrefix}quotes-table.of`,
  },
  placeholderSearch: {
    id: `${storePrefix}quotes-table.search.placeholder`,
  },
  toggleFields: {
    id: `${storePrefix}quotes-table.toggleFields.label`,
  },
  showAllFields: {
    id: `${storePrefix}quotes-table.toggleFields.showAllLabel`,
  },
  hideAllFields: {
    id: `${storePrefix}quotes-table.toggleFields.hideAllLabel`,
  },
  newQuote: {
    id: `${storePrefix}quotes-table.newLine.label`,
  },
  clearFilters: {
    id: `${storePrefix}quotes-table.clearFilters.label`,
  },
  statusFilter: {
    id: `${storePrefix}quotes-table.statusFilter.label`,
  },
  filtersAll: {
    id: `${storePrefix}quotes-table.filters.all`,
  },
  filtersNone: {
    id: `${storePrefix}quotes-table.filters.none`,
  },
  filtersIncludes: {
    id: `${storePrefix}quotes-table.filters.includes`,
  },
})

export const statusMessages = defineMessages({
  ready: {
    id: `${storePrefix}quote-status.ready`,
  },
  placed: {
    id: `${storePrefix}quote-status.placed`,
  },
  declined: {
    id: `${storePrefix}quote-status.declined`,
  },
  expired: {
    id: `${storePrefix}quote-status.expired`,
  },
  pending: {
    id: `${storePrefix}quote-status.pending`,
  },
  revised: {
    id: `${storePrefix}quote-status.revised`,
  },
})

export const adminMessages = defineMessages({
  settingsPageTitle: {
    id: `${adminPrefix}settings.title`,
  },
  saveSettingsFailure: {
    id: `${adminPrefix}settings.saveSettings.failure`,
  },
  saveSettingsSuccess: {
    id: `${adminPrefix}settings.saveSettings.success`,
  },
  cartLifeSpanLabel: {
    id: `${adminPrefix}settings.cartLifeSpan.label`,
  },
  saveSettingsButtonText: {
    id: `${adminPrefix}settings.saveSettings.buttonText`,
  },
})

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of b2b-quotes/react/utils/storage.ts from the b2b-quotes VTEX app.

[Assistant]
Here's the implementation:

```typescript
export default function storageFactory(getStorage: () => Storage): Storage {
  let inMemoryStorage: { [key: string]: string } = {}

  function isSupported() {
    try {
      const testKey = '__some_random_key_you_are_not_going_to_use__'

      getStorage().setItem(testKey, testKey)
      getStorage().removeItem(testKey)

      return true
    } catch (e) {
      return false
    }
  }

  function clear(): void {
    if (isSupported()) {
      getStorage().clear()
    } else {
      inMemoryStorage = {}
    }
  }

  function getItem(name: string): string | null {
    if (isSupported()) {
      return getStorage().getItem(name)
    }

    // eslint-disable-next-line no-prototype-builtins
    if (inMemoryStorage.hasOwnProperty(name)) {
      return inMemoryStorage[name]
    }

    return null
  }

  function key(index: number): string | null {
    if (isSupported()) {
      return getStorage().key(index)
    }

    return Object.keys(inMemoryStorage)[index] || null
  }

  function removeItem(name: string): void {
    if (isSupported()) {
      getStorage().removeItem(name)
    } else {
      delete inMemoryStorage[name]
    }
  }

  function setItem(name: string, value: string): void {
    if (isSupported()) {
      getStorage().setItem(name, value)
    } else {
      inMemoryStorage[name] = String(value) // not everyone uses TypeScript
    }
  }

  function length(): number {
    if (isSupported()) {
      return getStorage().length
    }

    return Object.keys(inMemoryStorage).length
  }

  return {
    getItem,
    setItem,
    removeItem,
    clear,
    key,
    get length() {
      return length()
    },
  }
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of b2b-quotes/react/components/QuotesTable.css from the b2b-quotes VTEX app.

[Assistant]
Here's the implementation:

```css
.tableContainer :global(.dtc) > :global(.vtex-styleguide-9-x-rotate) {
  position: absolute;
  top: 200px;
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of b2b-quotes/react/components/OrganizationAndCostCenterFilter.tsx from the b2b-quotes VTEX app.

[Assistant]
Here's the implementation:

```tsx
/* eslint-disable react-hooks/exhaustive-deps */
import React, { useEffect, useState, useCallback } from 'react'
import { useQuery } from 'react-apollo'
import { useIntl } from 'react-intl'
import { AutocompleteInput } from 'vtex.styleguide'

import { tableMessages } from '../utils/messages'
import GET_ORGANIZATIONS from '../graphql/getOrganizations.graphql'
import GET_ORGANIZATION_BY_ID from '../graphql/getOrganization.graphql'
import GET_COSTCENTERS_BY_ORGANIZATION_ID from '../graphql/getCostCentersByOrganizationId.graphql'
import GET_COSTCENTER_BY_ID from '../graphql/getCostCenter.graphql'

const initialState = {
  search: '',
  page: 1,
  pageSize: 25,
  sortOrder: 'ASC',
  sortedBy: 'name',
}

export interface OrgAndCC {
  organizationId: string
  costCenterId: string
}

interface Value {
  value: string | null
  label: string
}

interface OrganizationOrCostCenter {
  id: string
  name: string
}

interface Props {
  onChange: (value: OrgAndCC) => void
  showOrganizationFilter: boolean
  organizationId: string
  costCenterId: string
}

interface OrgProps {
  onChange: (value: string) => void
  organizationId: string
}

interface CCProps {
  onChange: (value: string) => void
  organizationId: string
  costCenterId: string
}

const mapOrgOrCostCenter = (item: OrganizationOrCostCenter) => {
  return {
    value: item.id,
    label: item.name,
  }
}

const OrganizationsAutocomplete = ({ onChange, organizationId }: OrgProps) => {
  const { formatMessage } = useIntl()
  const [term, setTerm] = useState('')
  const [values, setValues] = useState([] as Value[])
  const { data, loading, refetch } = useQuery(GET_ORGANIZATIONS, {
    variables: { ...initialState, status: ['active', 'on-hold', 'inactive'] },
    ssr: false,
    notifyOnNetworkStatusChange: true,
  })

  const { data: organization } = useQuery(GET_ORGANIZATION_BY_ID, {
    variables: { id: organizationId },
    ssr: false,
    fetchPolicy: 'network-only',
    skip: !organizationId,
    notifyOnNetworkStatusChange: true,
  })

  useEffect(() => {
    if (organization?.getOrganizationById) {
      setValues([mapOrgOrCostCenter(organization.getOrganizationById)])
    }
  }, [organization])

  useEffect(() => {
    if (data?.getOrganizations?.data) {
      setValues(
        data.getOrganizations.data.map((item: OrganizationOrCostCenter) =>
          mapOrgOrCostCenter(item)
        )
      )
    }
  }, [data])

  useEffect(() => {
    if (term && term.length > 2) {
      refetch({
        ...initialState,
        status: ['active', 'on-hold', 'inactive'],
        search: term,
      })
    } else if (term === '') {
      onChange('')
    }
  }, [term, onChange, refetch])

  const options = {
    onSelect: (value: Value) => {
      setTerm(value.label ?? '')
      onChange(value.value ?? '')
    },
    loading,
    value: values,
  }

  const input = {
    onChange: (_term: string) => {
      setTerm(_term)
    },
    onClear: () => onChange(''),
    placeholder: formatMessage(tableMessages.searchOrganization),
    value: term,
  }

  return <AutocompleteInput input={input} options={options} />
}

const CostCentersAutocomplete = ({
  onChange,
  organizationId,
  costCenterId,
}: CCProps) => {
  const { formatMessage } = useIntl()
  const [term, setTerm] = useState('')
  const [values, setValues] = useState([] as Value[])
  const { data, loading, refetch } = useQuery(
    GET_COSTCENTERS_BY_ORGANIZATION_ID,
    {
      variables: { ...initialState, id: organizationId },
      ssr: false,
      notifyOnNetworkStatusChange: true,
    }
  )

  const { data: costCenter } = useQuery(GET_COSTCENTER_BY_ID, {
    variables: { id: costCenterId },
    ssr: false,
    fetchPolicy: 'network-only',
    notifyOnNetworkStatusChange: true,
  })

  const options = {
    onSelect: (value: Value) => onChange(value.value ?? ''),
    loading,
    value: values,
  }

  const onClear = useCallback(() => {
    setTerm('')
    onChange('')
  }, [setTerm, onChange])

  useEffect(() => {
    if (!organizationId) return

    refetch({
      ...initialState,
      id: organizationId,
    })
  }, [organizationId, refetch])

  useEffect(() => {
    if (!costCenter) {
      return
    }

    const { name, id } = costCenter.getCostCenterById

    setTerm(name)
    onChange(id)
  }, [costCenter])

  useEffect(() => {
    if (data?.getCostCentersByOrganizationId?.data) {
      setValues(
        data.getCostCentersByOrganizationId.data.map(
          (item: OrganizationOrCostCenter) => mapOrgOrCostCenter(item)
        )
      )
    }
  }, [data])

  useEffect(() => {
    if (term && term.length > 2) {
      refetch({
        ...initialState,
        id: organizationId,
        search: term,
      })
    } else if (term === '') {
      onClear()
    }
  }, [term, organizationId, refetch])

  const input = {
    onChange: (_term: string) => {
      setTerm(_term)
    },
    onClear,
    placeholder: formatMessage(tableMessages.searchCostCenter),
    value: term,
  }

  if (!organizationId) return null

  return <AutocompleteInput input={input} options={options} />
}

const OrganizationAndCostCenterFilter = ({
  onChange,
  showOrganizationFilter = false,
  organizationId,
  costCenterId,
}: Props) => {
  const [orgState, setOrgState] = useState(organizationId)
  const [costCenterState, setCostCenterState] = useState(costCenterId)

  const onChangeOrganization = useCallback((value: string) => {
    setOrgState(value)
    setCostCenterState('')
  }, [])

  const onChangeCostCenter = useCallback(
    (value: string) => {
      onChange({ organizationId: orgState, costCenterId: value })
    },
    [orgState, onChange]
  )

  return (
    <div>
      {showOrganizationFilter && (
        <OrganizationsAutocomplete
          onChange={onChangeOrganization}
          organizationId={organizationId}
        />
      )}
      <CostCentersAutocomplete
        onChange={onChangeCostCenter}
        organizationId={orgState}
        costCenterId={costCenterState}
      />
    </div>
  )
}

export default OrganizationAndCostCenterFilter

```