mirror of
https://gitlab.com/bramw/baserow.git
synced 2025-04-07 06:15:36 +00:00
[2/3] Import Airtable view colors
This commit is contained in:
parent
29bfe98e54
commit
a11883ca0c
6 changed files with 457 additions and 19 deletions
backend
src/baserow/contrib/database/airtable
tests/baserow/contrib/database/airtable
changelog/entries/unreleased/feature
|
@ -13,16 +13,42 @@ AIRTABLE_EXPORT_JOB_DOWNLOADING_BASE = "downloading-base"
|
|||
AIRTABLE_EXPORT_JOB_CONVERTING = "converting"
|
||||
AIRTABLE_EXPORT_JOB_DOWNLOADING_FILES = "downloading-files"
|
||||
AIRTABLE_BASEROW_COLOR_MAPPING = {
|
||||
"blue": "blue",
|
||||
"cyan": "light-blue",
|
||||
"teal": "light-green",
|
||||
"green": "green",
|
||||
"yellow": "light-orange",
|
||||
"orange": "orange",
|
||||
"blue": "light-blue",
|
||||
"cyan": "light-cyan",
|
||||
"teal": "light-pink", # Baserow doesn't have teal, so we're using the left-over color
|
||||
"green": "light-green",
|
||||
"yellow": "light-yellow",
|
||||
"orange": "light-orange",
|
||||
"red": "light-red",
|
||||
"pink": "red",
|
||||
"purple": "dark-blue",
|
||||
"purple": "light-purple",
|
||||
"gray": "light-gray",
|
||||
"blueMedium": "blue",
|
||||
"cyanMedium": "cyan",
|
||||
"tealMedium": "pink",
|
||||
"greenMedium": "green",
|
||||
"yellowMedium": "yellow",
|
||||
"orangeMedium": "orange",
|
||||
"redMedium": "red",
|
||||
"purpleMedium": "purple",
|
||||
"grayMedium": "gray",
|
||||
"blueDark": "dark-blue",
|
||||
"cyanDark": "dark-cyan",
|
||||
"tealDark": "dark-pink",
|
||||
"greenDark": "dark-green",
|
||||
"yellowDark": "dark-yellow",
|
||||
"orangeDark": "dark-orange",
|
||||
"redDark": "dark-red",
|
||||
"purpleDark": "dark-purple",
|
||||
"grayDark": "dark-gray",
|
||||
"blueDarker": "darker-blue",
|
||||
"cyanDarker": "darker-cyan",
|
||||
"tealDarker": "darker-pink",
|
||||
"greenDarker": "darker-green",
|
||||
"yellowDarker": "darker-yellow",
|
||||
"orangeDarker": "darker-orange",
|
||||
"redDarker": "darker-red",
|
||||
"purpleDarker": "darker-purple",
|
||||
"grayDarker": "darker-gray",
|
||||
}
|
||||
AIRTABLE_NUMBER_FIELD_SEPARATOR_FORMAT_MAPPING = {
|
||||
"commaPeriod": "COMMA_PERIOD",
|
||||
|
|
|
@ -25,17 +25,20 @@ SCOPE_VIEW_GROUP_BY = SelectOption(
|
|||
SCOPE_VIEW_FILTER = SelectOption(
|
||||
id="scope_view_filter", value="View filter", color="light-pink", order=6
|
||||
)
|
||||
SCOPE_VIEW_COLOR = SelectOption(
|
||||
id="scope_view_color", value="View color", color="light-gray", order=7
|
||||
)
|
||||
SCOPE_VIEW_FIELD_OPTIONS = SelectOption(
|
||||
id="scope_view_field_options",
|
||||
value="View field options",
|
||||
color="light-purple",
|
||||
order=7,
|
||||
order=8,
|
||||
)
|
||||
SCOPE_AUTOMATIONS = SelectOption(
|
||||
id="scope_automations", value="Automations", color="light-orange", order=8
|
||||
id="scope_automations", value="Automations", color="light-orange", order=9
|
||||
)
|
||||
SCOPE_INTERFACES = SelectOption(
|
||||
id="scope_interfaces", value="Interfaces", color="light-yellow", order=9
|
||||
id="scope_interfaces", value="Interfaces", color="light-yellow", order=10
|
||||
)
|
||||
ALL_SCOPES = [
|
||||
SCOPE_FIELD,
|
||||
|
@ -44,6 +47,7 @@ ALL_SCOPES = [
|
|||
SCOPE_VIEW_SORT,
|
||||
SCOPE_VIEW_GROUP_BY,
|
||||
SCOPE_VIEW_FILTER,
|
||||
SCOPE_VIEW_COLOR,
|
||||
SCOPE_AUTOMATIONS,
|
||||
SCOPE_INTERFACES,
|
||||
]
|
||||
|
|
|
@ -1,14 +1,25 @@
|
|||
from typing import Any, Dict, List, Optional, Tuple, Union
|
||||
|
||||
from baserow_premium.views.decorator_types import LeftBorderColorDecoratorType
|
||||
from baserow_premium.views.decorator_value_provider_types import (
|
||||
ConditionalColorValueProviderType,
|
||||
SelectColorValueProviderType,
|
||||
)
|
||||
|
||||
from baserow.contrib.database.airtable.config import AirtableImportConfig
|
||||
from baserow.contrib.database.airtable.constants import AIRTABLE_ASCENDING_MAP
|
||||
from baserow.contrib.database.airtable.constants import (
|
||||
AIRTABLE_ASCENDING_MAP,
|
||||
AIRTABLE_BASEROW_COLOR_MAPPING,
|
||||
)
|
||||
from baserow.contrib.database.airtable.exceptions import (
|
||||
AirtableSkipCellValue,
|
||||
AirtableSkipFilter,
|
||||
)
|
||||
from baserow.contrib.database.airtable.import_report import (
|
||||
ERROR_TYPE_DATA_TYPE_MISMATCH,
|
||||
ERROR_TYPE_UNSUPPORTED_FEATURE,
|
||||
SCOPE_FIELD,
|
||||
SCOPE_VIEW_COLOR,
|
||||
SCOPE_VIEW_FILTER,
|
||||
SCOPE_VIEW_GROUP_BY,
|
||||
SCOPE_VIEW_SORT,
|
||||
|
@ -27,6 +38,7 @@ from baserow.contrib.database.views.models import (
|
|||
SORT_ORDER_ASC,
|
||||
SORT_ORDER_DESC,
|
||||
View,
|
||||
ViewDecoration,
|
||||
ViewFilter,
|
||||
ViewFilterGroup,
|
||||
ViewGroupBy,
|
||||
|
@ -492,6 +504,174 @@ class AirtableViewType(Instance):
|
|||
else:
|
||||
return [baserow_filter], []
|
||||
|
||||
def get_select_column_decoration(
|
||||
self,
|
||||
field_mapping: dict,
|
||||
view_type: ViewType,
|
||||
row_id_mapping: Dict[str, Dict[str, int]],
|
||||
raw_airtable_table: dict,
|
||||
raw_airtable_view: dict,
|
||||
raw_airtable_view_data: dict,
|
||||
import_report: AirtableImportReport,
|
||||
) -> Optional[ViewDecoration]:
|
||||
color_config = raw_airtable_view_data["colorConfig"]
|
||||
select_column_id = color_config["selectColumnId"]
|
||||
|
||||
if select_column_id not in field_mapping:
|
||||
column_name = get_airtable_column_name(raw_airtable_table, select_column_id)
|
||||
import_report.add_failed(
|
||||
raw_airtable_view["name"],
|
||||
SCOPE_VIEW_COLOR,
|
||||
raw_airtable_table["name"],
|
||||
ERROR_TYPE_DATA_TYPE_MISMATCH,
|
||||
f'The select field coloring was ignored in {raw_airtable_view["name"]} '
|
||||
f"because {column_name} does not exist.",
|
||||
)
|
||||
return None
|
||||
|
||||
return ViewDecoration(
|
||||
id=f"{raw_airtable_view['id']}_decoration",
|
||||
view_id=raw_airtable_view["id"],
|
||||
type=LeftBorderColorDecoratorType.type,
|
||||
value_provider_type=SelectColorValueProviderType.type,
|
||||
value_provider_conf={"field_id": select_column_id},
|
||||
order=1,
|
||||
)
|
||||
|
||||
def get_color_definitions_decoration(
|
||||
self,
|
||||
field_mapping: dict,
|
||||
view_type: ViewType,
|
||||
row_id_mapping: Dict[str, Dict[str, int]],
|
||||
raw_airtable_table: dict,
|
||||
raw_airtable_view: dict,
|
||||
raw_airtable_view_data: dict,
|
||||
import_report: AirtableImportReport,
|
||||
) -> Optional[ViewDecoration]:
|
||||
color_config = raw_airtable_view_data["colorConfig"]
|
||||
color_definitions = color_config["colorDefinitions"]
|
||||
default_color = AIRTABLE_BASEROW_COLOR_MAPPING.get(
|
||||
color_config.get("defaultColor", ""),
|
||||
"",
|
||||
)
|
||||
baserow_colors = []
|
||||
|
||||
for color_definition in color_definitions:
|
||||
filters, filter_groups = self.get_filters(
|
||||
field_mapping,
|
||||
row_id_mapping,
|
||||
raw_airtable_view,
|
||||
raw_airtable_table,
|
||||
import_report,
|
||||
color_definition,
|
||||
)
|
||||
# Pop the first group because that shouldn't be in Baserow, and the type is
|
||||
# defined on the view.
|
||||
root_group = filter_groups.pop(0)
|
||||
color = AIRTABLE_BASEROW_COLOR_MAPPING.get(
|
||||
color_definition.get("color", ""),
|
||||
"blue",
|
||||
)
|
||||
baserow_colors.append(
|
||||
{
|
||||
"filter_groups": [
|
||||
{
|
||||
"id": filter_group.id,
|
||||
"filter_type": filter_group.filter_type,
|
||||
"parent_group": (
|
||||
None
|
||||
if filter_group.parent_group_id == root_group.id
|
||||
else filter_group.parent_group_id
|
||||
),
|
||||
}
|
||||
for filter_group in filter_groups
|
||||
],
|
||||
"filters": [
|
||||
{
|
||||
"id": filter_object.id,
|
||||
"type": filter_object.type,
|
||||
"field": filter_object.field_id,
|
||||
"group": (
|
||||
None
|
||||
if filter_object.group_id == root_group.id
|
||||
else filter_object.group_id
|
||||
),
|
||||
"value": filter_object.value,
|
||||
}
|
||||
for filter_object in filters
|
||||
],
|
||||
"operator": root_group.filter_type,
|
||||
"color": color,
|
||||
}
|
||||
)
|
||||
|
||||
if default_color != "":
|
||||
baserow_colors.append(
|
||||
{
|
||||
"filter_groups": [],
|
||||
"filters": [],
|
||||
"operator": "AND",
|
||||
"color": default_color,
|
||||
}
|
||||
)
|
||||
|
||||
return ViewDecoration(
|
||||
id=f"{raw_airtable_view['id']}_decoration",
|
||||
view_id=raw_airtable_view["id"],
|
||||
type=LeftBorderColorDecoratorType.type,
|
||||
value_provider_type=ConditionalColorValueProviderType.type,
|
||||
value_provider_conf={"colors": baserow_colors},
|
||||
order=1,
|
||||
)
|
||||
|
||||
def get_decorations(
|
||||
self,
|
||||
field_mapping: dict,
|
||||
view_type: ViewType,
|
||||
row_id_mapping: Dict[str, Dict[str, int]],
|
||||
raw_airtable_table: dict,
|
||||
raw_airtable_view: dict,
|
||||
raw_airtable_view_data: dict,
|
||||
import_report: AirtableImportReport,
|
||||
) -> List[ViewDecoration]:
|
||||
"""
|
||||
Converts the raw Airtable color config into matching Baserow view decorations.
|
||||
"""
|
||||
|
||||
color_config = raw_airtable_view_data.get("colorConfig", None)
|
||||
|
||||
if not view_type.can_decorate or color_config is None:
|
||||
return []
|
||||
|
||||
color_config_type = color_config.get("type", "")
|
||||
decoration = None
|
||||
|
||||
if color_config_type == "selectColumn":
|
||||
decoration = self.get_select_column_decoration(
|
||||
field_mapping,
|
||||
view_type,
|
||||
row_id_mapping,
|
||||
raw_airtable_table,
|
||||
raw_airtable_view,
|
||||
raw_airtable_view_data,
|
||||
import_report,
|
||||
)
|
||||
elif color_config_type == "colorDefinitions":
|
||||
decoration = self.get_color_definitions_decoration(
|
||||
field_mapping,
|
||||
view_type,
|
||||
row_id_mapping,
|
||||
raw_airtable_table,
|
||||
raw_airtable_view,
|
||||
raw_airtable_view_data,
|
||||
import_report,
|
||||
)
|
||||
|
||||
if decoration:
|
||||
return [decoration]
|
||||
else:
|
||||
return []
|
||||
|
||||
def to_serialized_baserow_view(
|
||||
self,
|
||||
field_mapping,
|
||||
|
@ -527,7 +707,7 @@ class AirtableViewType(Instance):
|
|||
import_report,
|
||||
filters_object,
|
||||
)
|
||||
# Pop the first group because that shouldn't in Baserow, and the type is
|
||||
# Pop the first group because that shouldn't be in Baserow, and the type is
|
||||
# defined on the view.
|
||||
view.filter_type = filter_groups.pop(0).filter_type
|
||||
|
||||
|
@ -547,6 +727,15 @@ class AirtableViewType(Instance):
|
|||
raw_airtable_view_data,
|
||||
import_report,
|
||||
)
|
||||
decorations = self.get_decorations(
|
||||
field_mapping,
|
||||
view_type,
|
||||
row_id_mapping,
|
||||
raw_airtable_table,
|
||||
raw_airtable_view,
|
||||
raw_airtable_view_data,
|
||||
import_report,
|
||||
)
|
||||
|
||||
view.get_field_options = lambda *args, **kwargs: []
|
||||
view._prefetched_objects_cache = {
|
||||
|
@ -554,7 +743,7 @@ class AirtableViewType(Instance):
|
|||
"filter_groups": filter_groups,
|
||||
"viewsort_set": sorts,
|
||||
"viewgroupby_set": group_bys,
|
||||
"viewdecoration_set": [],
|
||||
"viewdecoration_set": decorations,
|
||||
}
|
||||
view = self.prepare_view_object(
|
||||
field_mapping,
|
||||
|
@ -587,7 +776,7 @@ class AirtableViewType(Instance):
|
|||
Note that the common properties like name, filters, sorts, etc are added by
|
||||
default depending on the Baserow view support for it.
|
||||
|
||||
:param field_mapping: @TODO
|
||||
:param field_mapping: A dict containing all the imported fields.
|
||||
:param view: The view object that must be prepared.
|
||||
:param raw_airtable_table: The raw Airtable table data related to the column.
|
||||
:param raw_airtable_view: The raw Airtable view values that must be
|
||||
|
|
|
@ -1413,11 +1413,11 @@ def test_airtable_import_multi_select_column(
|
|||
assert len(select_options) == 2
|
||||
assert select_options[0].id == "fldURNo0cvi6YWYcYj1_selEOJmenvqEd6pndFQ"
|
||||
assert select_options[0].value == "Option 1"
|
||||
assert select_options[0].color == "blue"
|
||||
assert select_options[0].color == "light-blue"
|
||||
assert select_options[0].order == 1
|
||||
assert select_options[1].id == "fldURNo0cvi6YWYcYj1_sel5ekvuoNVvl03olMO"
|
||||
assert select_options[1].value == "Option 2"
|
||||
assert select_options[1].color == "light-blue"
|
||||
assert select_options[1].color == "light-cyan"
|
||||
assert select_options[1].order == 0
|
||||
|
||||
|
||||
|
@ -2368,11 +2368,11 @@ def test_airtable_import_select_column(
|
|||
assert len(select_options) == 2
|
||||
assert select_options[0].id == "fldRd2Vkzgsf6X4z6B4_selbh6rEWaaiyQvWyfg"
|
||||
assert select_options[0].value == "Option A"
|
||||
assert select_options[0].color == "blue"
|
||||
assert select_options[0].color == "light-blue"
|
||||
assert select_options[0].order == 0
|
||||
assert select_options[1].id == "fldRd2Vkzgsf6X4z6B4_selvZgpWhbkeRVphROT"
|
||||
assert select_options[1].value == "Option B"
|
||||
assert select_options[1].color == "light-blue"
|
||||
assert select_options[1].color == "light-cyan"
|
||||
assert select_options[1].order == 1
|
||||
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ import pytest
|
|||
|
||||
from baserow.contrib.database.airtable.config import AirtableImportConfig
|
||||
from baserow.contrib.database.airtable.import_report import (
|
||||
SCOPE_VIEW_COLOR,
|
||||
SCOPE_VIEW_GROUP_BY,
|
||||
SCOPE_VIEW_SORT,
|
||||
AirtableImportReport,
|
||||
|
@ -133,6 +134,62 @@ RAW_VIEW_DATA_GROUPS = [
|
|||
"emptyGroupState": "hidden",
|
||||
}
|
||||
]
|
||||
RAW_VIEW_COLOR_CONFIG_SELECT_COLUMN = {
|
||||
"type": "selectColumn",
|
||||
"selectColumnId": "fldwSc9PqedIhTSqhi1",
|
||||
"colorDefinitions": None,
|
||||
"defaultColor": None,
|
||||
}
|
||||
RAW_VIEW_COLOR_CONFIG_COLOR_DEFINITIONS = {
|
||||
"type": "colorDefinitions",
|
||||
"colorDefinitions": [
|
||||
{
|
||||
"filterSet": [
|
||||
{
|
||||
"id": "fltp2gabc8P91234f",
|
||||
"columnId": "fldwSc9PqedIhTSqhi1",
|
||||
"operator": "isNotEmpty",
|
||||
"value": None,
|
||||
},
|
||||
{
|
||||
"id": "flthuYL0uubbDF2Xy",
|
||||
"type": "nested",
|
||||
"conjunction": "and",
|
||||
"filterSet": [
|
||||
{
|
||||
"id": "flt70g1l245672xRi",
|
||||
"columnId": "fldwSc9PqedIhTSqhi1",
|
||||
"operator": "!=",
|
||||
"value": "test",
|
||||
},
|
||||
{
|
||||
"id": "fltVg238719fbIKqC",
|
||||
"columnId": "fldwSc9PqedIhTSqhi2",
|
||||
"operator": "!=",
|
||||
"value": "test2",
|
||||
},
|
||||
{
|
||||
"id": "flthuYL0uubbDF2Xz",
|
||||
"type": "nested",
|
||||
"conjunction": "or",
|
||||
"filterSet": [
|
||||
{
|
||||
"id": "flt70g1l245672xRi",
|
||||
"columnId": "fldwSc9PqedIhTSqhi1",
|
||||
"operator": "!=",
|
||||
"value": "test",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
"conjunction": "or",
|
||||
"color": "yellow",
|
||||
}
|
||||
],
|
||||
"defaultColor": "teal",
|
||||
}
|
||||
|
||||
|
||||
def test_import_grid_view():
|
||||
|
@ -508,3 +565,157 @@ def test_import_grid_view_filters_and_groups():
|
|||
assert serialized_view["filter_groups"] == [
|
||||
{"id": "flthuYL0uubbDF2Xy", "filter_type": "AND", "parent_group": None}
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_import_grid_view_color_config_select_column_not_existing_column():
|
||||
view_data = deepcopy(RAW_AIRTABLE_VIEW_DATA)
|
||||
field_mapping = deepcopy(FIELD_MAPPING)
|
||||
for field_object in field_mapping.values():
|
||||
field_object["baserow_field"].content_type = ContentType.objects.get_for_model(
|
||||
field_object["baserow_field"]
|
||||
)
|
||||
|
||||
view_data["colorConfig"] = deepcopy(RAW_VIEW_COLOR_CONFIG_SELECT_COLUMN)
|
||||
view_data["colorConfig"]["selectColumnId"] = "fld123"
|
||||
|
||||
airtable_view_type = airtable_view_type_registry.get("grid")
|
||||
import_report = AirtableImportReport()
|
||||
serialized_view = airtable_view_type.to_serialized_baserow_view(
|
||||
field_mapping,
|
||||
ROW_ID_MAPPING,
|
||||
RAW_AIRTABLE_TABLE,
|
||||
RAW_AIRTABLE_VIEW,
|
||||
view_data,
|
||||
AirtableImportConfig(),
|
||||
import_report,
|
||||
)
|
||||
assert len(import_report.items) == 1
|
||||
assert import_report.items[0].object_name == "Grid view"
|
||||
assert import_report.items[0].scope == SCOPE_VIEW_COLOR
|
||||
assert import_report.items[0].table == "Data"
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_import_grid_view_color_config_select_column():
|
||||
view_data = deepcopy(RAW_AIRTABLE_VIEW_DATA)
|
||||
field_mapping = deepcopy(FIELD_MAPPING)
|
||||
for field_object in field_mapping.values():
|
||||
field_object["baserow_field"].content_type = ContentType.objects.get_for_model(
|
||||
field_object["baserow_field"]
|
||||
)
|
||||
|
||||
view_data["colorConfig"] = RAW_VIEW_COLOR_CONFIG_SELECT_COLUMN
|
||||
|
||||
airtable_view_type = airtable_view_type_registry.get("grid")
|
||||
import_report = AirtableImportReport()
|
||||
serialized_view = airtable_view_type.to_serialized_baserow_view(
|
||||
field_mapping,
|
||||
ROW_ID_MAPPING,
|
||||
RAW_AIRTABLE_TABLE,
|
||||
RAW_AIRTABLE_VIEW,
|
||||
view_data,
|
||||
AirtableImportConfig(),
|
||||
import_report,
|
||||
)
|
||||
assert len(import_report.items) == 0
|
||||
|
||||
assert serialized_view["decorations"] == [
|
||||
{
|
||||
"id": "viwcpYeEpAs6kZspktV_decoration",
|
||||
"type": "left_border_color",
|
||||
"value_provider_type": "single_select_color",
|
||||
"value_provider_conf": {"field_id": "fldwSc9PqedIhTSqhi1"},
|
||||
"order": 1,
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_import_grid_view_color_config_color_definitions():
|
||||
view_data = deepcopy(RAW_AIRTABLE_VIEW_DATA)
|
||||
field_mapping = deepcopy(FIELD_MAPPING)
|
||||
for field_object in field_mapping.values():
|
||||
field_object["baserow_field"].content_type = ContentType.objects.get_for_model(
|
||||
field_object["baserow_field"]
|
||||
)
|
||||
|
||||
view_data["colorConfig"] = RAW_VIEW_COLOR_CONFIG_COLOR_DEFINITIONS
|
||||
|
||||
airtable_view_type = airtable_view_type_registry.get("grid")
|
||||
import_report = AirtableImportReport()
|
||||
serialized_view = airtable_view_type.to_serialized_baserow_view(
|
||||
field_mapping,
|
||||
ROW_ID_MAPPING,
|
||||
RAW_AIRTABLE_TABLE,
|
||||
RAW_AIRTABLE_VIEW,
|
||||
view_data,
|
||||
AirtableImportConfig(),
|
||||
import_report,
|
||||
)
|
||||
assert len(import_report.items) == 0
|
||||
|
||||
assert serialized_view["decorations"] == [
|
||||
{
|
||||
"id": "viwcpYeEpAs6kZspktV_decoration",
|
||||
"type": "left_border_color",
|
||||
"value_provider_type": "conditional_color",
|
||||
"value_provider_conf": {
|
||||
"colors": [
|
||||
{
|
||||
"filter_groups": [
|
||||
{
|
||||
"id": "flthuYL0uubbDF2Xy",
|
||||
"filter_type": "AND",
|
||||
"parent_group": None,
|
||||
},
|
||||
{
|
||||
"id": "flthuYL0uubbDF2Xz",
|
||||
"filter_type": "OR",
|
||||
"parent_group": "flthuYL0uubbDF2Xy",
|
||||
},
|
||||
],
|
||||
"filters": [
|
||||
{
|
||||
"id": "fltp2gabc8P91234f",
|
||||
"type": "not_empty",
|
||||
"field": "fldwSc9PqedIhTSqhi1",
|
||||
"group": None,
|
||||
"value": "",
|
||||
},
|
||||
{
|
||||
"id": "flt70g1l245672xRi",
|
||||
"type": "not_equal",
|
||||
"field": "fldwSc9PqedIhTSqhi1",
|
||||
"group": "flthuYL0uubbDF2Xy",
|
||||
"value": "test",
|
||||
},
|
||||
{
|
||||
"id": "fltVg238719fbIKqC",
|
||||
"type": "not_equal",
|
||||
"field": "fldwSc9PqedIhTSqhi2",
|
||||
"group": "flthuYL0uubbDF2Xy",
|
||||
"value": "test2",
|
||||
},
|
||||
{
|
||||
"id": "flt70g1l245672xRi",
|
||||
"type": "not_equal",
|
||||
"field": "fldwSc9PqedIhTSqhi1",
|
||||
"group": "flthuYL0uubbDF2Xz",
|
||||
"value": "test",
|
||||
},
|
||||
],
|
||||
"operator": "OR",
|
||||
"color": "light-yellow",
|
||||
},
|
||||
{
|
||||
"filter_groups": [],
|
||||
"filters": [],
|
||||
"operator": "AND",
|
||||
"color": "light-pink",
|
||||
},
|
||||
]
|
||||
},
|
||||
"order": 1,
|
||||
}
|
||||
]
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"type": "feature",
|
||||
"message": "Import Airtable view colors.",
|
||||
"domain": "database",
|
||||
"issue_number": 793,
|
||||
"bullet_points": [],
|
||||
"created_at": "2025-03-13"
|
||||
}
|
Loading…
Add table
Reference in a new issue