Compare commits
1 Commits
main
...
fix/barent
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
494e4445be |
@@ -107,3 +107,5 @@ importSideEffects "@spectrum-web-components/icons-workflow/icons/sp-icon-rotate-
|
||||
importSideEffects "@spectrum-web-components/icons-workflow/icons/sp-icon-align-bottom.js"
|
||||
importSideEffects "@spectrum-web-components/icons-workflow/icons/sp-icon-transform-perspective.js"
|
||||
importSideEffects "@spectrum-web-components/icons-workflow/icons/sp-icon-search.js"
|
||||
importSideEffects "@spectrum-web-components/icons-workflow/icons/sp-icon-location.js"
|
||||
importSideEffects "@spectrum-web-components/icons-workflow/icons/sp-icon-list-bulleted.js"
|
||||
|
||||
@@ -290,6 +290,10 @@ let fiskeri (topic: InfoLayer) : Ol.Layer.Layer =
|
||||
source.serverType Source.ServerType.Geoserver
|
||||
source.params' !!{| layers = string topic |}
|
||||
]
|
||||
if topic = POs then
|
||||
let parameters = source.getParams ()
|
||||
console.log $"PARAMS : %A{parameters}"
|
||||
|
||||
Layer.imageLayer [
|
||||
layer.source source
|
||||
layer.opacity alpha
|
||||
|
||||
@@ -6,6 +6,7 @@ open Fable.Core
|
||||
open Fable.Core.JsInterop
|
||||
open Fable.OpenLayers
|
||||
open Proj4
|
||||
open System.Text.RegularExpressions
|
||||
|
||||
proj4.defs ("EPSG:25832", "+proj=utm +zone=32 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs")
|
||||
proj4.defs ("EPSG:25833", "+proj=utm +zone=33 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs")
|
||||
@@ -23,6 +24,18 @@ let inline coordToPos (coord : Coordinate) = coord[0], coord[1]
|
||||
|
||||
let inline tryMaxDefault d s = if (Seq.isEmpty s) then d else Seq.max s
|
||||
let inline tryParseFloat (s: string) : float option = s |> Double.TryParse |> function | true, f -> Some f | _ -> None
|
||||
let inline tryParseInt (s: string) : int option = s |> Int32.TryParse |> function | true, i -> Some i | _ -> None
|
||||
|
||||
// Extracting first number in string together with its prefix: "Ab4cdef" -> Some ("Ab", 4)
|
||||
let inline trySplitPrefixAndInt (input: string) : (string * int) option =
|
||||
let m = Regex.Match(input, @"^([A-Za-z]+)(\d+)$")
|
||||
if m.Success then
|
||||
let prefix = m.Groups.[1].Value
|
||||
let number = int m.Groups.[2].Value
|
||||
Some (prefix, number)
|
||||
else
|
||||
None
|
||||
|
||||
|
||||
let formatDigits n m = {| minimumFractionDigits = n; maximumFractionDigits = m |} |> JS.JSON.stringify
|
||||
let formatPercent n = {| style = "percent"; minimumFractionDigits = n; maximumFractionDigits = n |} |> JS.JSON.stringify
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
module Fiskeridir
|
||||
|
||||
open Browser
|
||||
open Fable.Core
|
||||
open Fable.OpenLayers
|
||||
open Thoth.Fetch
|
||||
open Thoth.Json
|
||||
@@ -8,9 +9,91 @@ open Thoth.Json
|
||||
open Atlantis.Types
|
||||
open Utils
|
||||
|
||||
type ProductionAreaData = {
|
||||
DefaultCode: string
|
||||
DefaultName: string
|
||||
} with
|
||||
static member empty = {
|
||||
DefaultCode = ""
|
||||
DefaultName = ""
|
||||
}
|
||||
member x.ToLabel() = $"PO{x.DefaultCode}: {x.DefaultName}"
|
||||
|
||||
type SiteData = {
|
||||
// SiteId: int
|
||||
// VersionId: int
|
||||
SiteNr: int
|
||||
Name: string
|
||||
// PlacementType: string
|
||||
// PlacementTypeValue: string
|
||||
// WaterType: string
|
||||
// WaterTypeValue: string
|
||||
// FirstClearanceTime: string
|
||||
// FirstClearanceType: string
|
||||
// FirstClearanceTypeValue: string
|
||||
Latitude: float
|
||||
Longitude: float
|
||||
Capacity: float
|
||||
// TempCapacity: float
|
||||
// CapacityUnitType: string
|
||||
Placement: Placement
|
||||
// SpeciesType: string
|
||||
// SpeciesTypeValue: string
|
||||
// SpeciesLimitations: SpeciesLimitations list
|
||||
// Connections: Connections list
|
||||
// ObsoleteConnections: Connections list
|
||||
// Version: Version
|
||||
// IsSlaughtery: bool
|
||||
// HasCommercialActivity: bool
|
||||
// HasColocation: bool
|
||||
// HasJointOperation: bool
|
||||
}
|
||||
and Placement = {
|
||||
// MunicipalityCode: string
|
||||
MunicipalityName: string
|
||||
// CountyCode: string
|
||||
// CountyName: string
|
||||
ProdAreaCode: string
|
||||
ProdAreaName: string
|
||||
// ProdAreaStatus: string
|
||||
}
|
||||
// and SpeciesLimitations = {
|
||||
// Code: string
|
||||
// LatinName: string
|
||||
// NbNoName: string
|
||||
// NnNoName: string
|
||||
// EnGbName: string
|
||||
// }
|
||||
// and private Connections = {
|
||||
// Key: string
|
||||
// LicenseId: int
|
||||
// LicenseNr: string
|
||||
// SiteId: int
|
||||
// SiteNr: string
|
||||
// SiteName: string
|
||||
// ValidFrom: string
|
||||
// ValidUntil: string
|
||||
// TemporaryUntil: string option
|
||||
// RegisteredTime: string
|
||||
// Active: bool
|
||||
// Status: string
|
||||
// StatusValue: string
|
||||
// }
|
||||
// and private Version = {
|
||||
// ValidFrom: string
|
||||
// ValidUntil: string
|
||||
// TemporaryUntil: string option
|
||||
// RegisteredTime: string
|
||||
// VersionCauseType: string
|
||||
// VersionCauseTypeValue: string
|
||||
// Status: string
|
||||
// StatusValue: string
|
||||
// VersionableStatus: string
|
||||
// VersionableStatusValue: string
|
||||
// }
|
||||
|
||||
/// See docs/fiskeridir-locality-borders-example-payload.json
|
||||
type FiskeridirLocalityBorder = {
|
||||
type LocalityBorder = {
|
||||
Id : int
|
||||
SiteVersionId : int
|
||||
SiteNr : int
|
||||
@@ -27,14 +110,15 @@ and Point = {
|
||||
}
|
||||
|
||||
let fetchLocalityBorders (map: OlMap) (localityId: int) =
|
||||
Fetch.tryGet<unit, FiskeridirLocalityBorder array>(
|
||||
console.log $"FETCHING LOCALITY BORDERS"
|
||||
Fetch.tryGet<unit, LocalityBorder array>(
|
||||
url = $"https://api.fiskeridir.no/pub-aqua/api/v1/sites/{localityId}/borders",
|
||||
headers = [
|
||||
Fetch.Types.HttpRequestHeaders.Accept "application/json"
|
||||
Fetch.Types.HttpRequestHeaders.AcceptCharset "UTF-8"
|
||||
],
|
||||
decoder =
|
||||
Decode.Auto.generateDecoder<FiskeridirLocalityBorder array>(
|
||||
Decode.Auto.generateDecoder<LocalityBorder array>(
|
||||
caseStrategy = CaseStrategy.CamelCase
|
||||
)
|
||||
)
|
||||
@@ -42,6 +126,8 @@ let fetchLocalityBorders (map: OlMap) (localityId: int) =
|
||||
match res with
|
||||
| Error err -> console.error("Could not fetch locality borders from api.fiskeridir.no:", err)
|
||||
| Ok borders ->
|
||||
console.log $"BORDERS LENGTH : {borders.Length}"
|
||||
console.log $"BORDER[0] : {borders[0]}"
|
||||
let points =
|
||||
borders
|
||||
|> Array.collect (fun border ->
|
||||
@@ -95,3 +181,121 @@ let fetchLocalityBorders (map: OlMap) (localityId: int) =
|
||||
Layers.addFeatures map MapLayer.Aquaculture [| feature |]
|
||||
|
||||
())
|
||||
|
||||
let fetchInfoPO (areaCode: int) : ProductionAreaData JS.Promise =
|
||||
console.log $"Fetching Production Area data: PO{areaCode}"
|
||||
|
||||
Fetch.tryGet<unit, ProductionAreaData>(
|
||||
url = $"https://api.fiskeridir.no/pub-aqua/api/v1/areas/production_area/{string areaCode}",
|
||||
headers = [
|
||||
Fetch.Types.HttpRequestHeaders.Accept "application/json"
|
||||
Fetch.Types.HttpRequestHeaders.AcceptCharset "UTF-8"
|
||||
],
|
||||
decoder =
|
||||
Decode.Auto.generateDecoder<ProductionAreaData>(
|
||||
caseStrategy = CaseStrategy.CamelCase
|
||||
)
|
||||
)
|
||||
|> Promise.map (fun res ->
|
||||
match res with
|
||||
| Error err ->
|
||||
console.error("Could not fetch site data from api.fiskeridir.no:", err)
|
||||
ProductionAreaData.empty
|
||||
| Ok po ->
|
||||
console.log $"Fetched PO{po.DefaultCode} : {po.DefaultName}"
|
||||
po)
|
||||
|
||||
let fetchSitesPO (range: int*int) (po: int) : SiteData [] JS.Promise =
|
||||
console.log $"Fetching Aquaculture Sites : PO{po} [{fst range} - {snd range}]"
|
||||
|
||||
Fetch.tryGet<unit, SiteData[]>(
|
||||
url = $"https://api.fiskeridir.no/pub-aqua/api/v1/sites?production-area-code={string po}&range={fst range}-{snd range}",
|
||||
headers = [
|
||||
Fetch.Types.HttpRequestHeaders.Accept "application/json"
|
||||
Fetch.Types.HttpRequestHeaders.AcceptCharset "UTF-8"
|
||||
],
|
||||
decoder =
|
||||
Decode.Auto.generateDecoder<SiteData[]>(
|
||||
caseStrategy = CaseStrategy.CamelCase
|
||||
)
|
||||
)
|
||||
|> Promise.map (fun res ->
|
||||
match res with
|
||||
| Error err ->
|
||||
console.error("Could not fetch site data from api.fiskeridir.no:", err)
|
||||
Array.empty
|
||||
| Ok sites ->
|
||||
console.log $"Fetched {sites.Length} sites in PO{po}"
|
||||
sites)
|
||||
|
||||
let fetchSiteData (map: OlMap) (localityId: int) =
|
||||
console.log $"Fetching Aquaculture Site : {localityId}"
|
||||
Fetch.tryGet<unit, SiteData>(
|
||||
url = $"https://api.fiskeridir.no/pub-aqua/api/v1/sites/{localityId}",
|
||||
headers = [
|
||||
Fetch.Types.HttpRequestHeaders.Accept "application/json"
|
||||
Fetch.Types.HttpRequestHeaders.AcceptCharset "UTF-8"
|
||||
],
|
||||
decoder =
|
||||
Decode.Auto.generateDecoder<SiteData>(
|
||||
caseStrategy = CaseStrategy.CamelCase
|
||||
)
|
||||
)
|
||||
|> Promise.map (fun res ->
|
||||
match res with
|
||||
| Error err -> console.error("Could not fetch site data from api.fiskeridir.no:", err)
|
||||
| Ok site ->
|
||||
console.log $"SITE : {site}"
|
||||
// let points =
|
||||
// borders
|
||||
// |> Array.collect (fun border ->
|
||||
// border.Points)
|
||||
//
|
||||
// if points.Length < 1 then
|
||||
// // TODO: Add some default geom for sites without bordes
|
||||
// console.info("This site does not have borders")
|
||||
// ()
|
||||
// else
|
||||
// let feature =
|
||||
// let coords : Coordinate array =
|
||||
// let coords =
|
||||
// points
|
||||
// |> Array.map (fun point ->
|
||||
// posToCoord (point.Longitude, point.Latitude)
|
||||
// |> coordToEpsg3857)
|
||||
// let first = Array.head coords
|
||||
// let last = coords[coords.Length - 1]
|
||||
//
|
||||
// if first |> Coord.equals last then
|
||||
// coords
|
||||
// else
|
||||
// let first = coords |> Array.head |> Array.singleton
|
||||
// let linearRing = first |> Array.append coords
|
||||
// linearRing
|
||||
//
|
||||
// let polygon =
|
||||
// Geometry.polygon [
|
||||
// geometry.coordinates [| coords |]
|
||||
// geometry.layout GeometryLayout.XY
|
||||
// ]
|
||||
// Feature.feature [
|
||||
// feature.geometryOrProperties polygon
|
||||
// ]
|
||||
//
|
||||
// feature.setId localityId
|
||||
// feature.set("type", "polygon")
|
||||
//
|
||||
// // NOTE: Fly to feature
|
||||
// let geom = feature.getGeometry()
|
||||
// let extent = geom.getExtent ()
|
||||
// let center = extent |> Extent.extent.getCenter
|
||||
// let view = map.getView()
|
||||
// let res = view.getResolutionForExtent extent
|
||||
// let zoom = view.getZoomForResolution res
|
||||
// let clamped = System.Double.Clamp(zoom, 7.0, 14.0)
|
||||
// console.debug("Flying to aquaculture area", zoom, clamped)
|
||||
// Maps.flyTo map clamped center
|
||||
//
|
||||
// Layers.addFeatures map MapLayer.Aquaculture [| feature |]
|
||||
|
||||
())
|
||||
|
||||
329
src/Atlantis/src/Client/Mapster/LocalitiesDialog.fs
Normal file
329
src/Atlantis/src/Client/Mapster/LocalitiesDialog.fs
Normal file
@@ -0,0 +1,329 @@
|
||||
module LocalitiesDialog
|
||||
|
||||
open System
|
||||
open Browser
|
||||
open Fable.Core
|
||||
open Fable.Core.JsInterop
|
||||
open Lit
|
||||
|
||||
open Atlantis
|
||||
open Atlantis.Types
|
||||
open Archmaester.Dto
|
||||
open Maps
|
||||
open Model
|
||||
open Utils
|
||||
|
||||
type private TableItem = {
|
||||
id: int
|
||||
name: string
|
||||
species: string
|
||||
capacity: float
|
||||
latitude: float
|
||||
longitude: float
|
||||
municipality: string
|
||||
prodAreaName: string
|
||||
prodAreaCode: int
|
||||
liceCoverage: SimCoverage
|
||||
wcCoverage: SimCoverage
|
||||
} with
|
||||
static member empty = {
|
||||
id = 0
|
||||
name = ""
|
||||
species = ""
|
||||
capacity = 0.0
|
||||
latitude = 0.0
|
||||
longitude = 0.0
|
||||
municipality = ""
|
||||
prodAreaName = ""
|
||||
prodAreaCode = 0
|
||||
liceCoverage = SimCoverage.empty
|
||||
wcCoverage = SimCoverage.empty
|
||||
}
|
||||
and private SimCoverage = {
|
||||
progress: float
|
||||
state: SimState
|
||||
eta: float
|
||||
} with
|
||||
static member empty = {
|
||||
progress = 0.0
|
||||
state = SimState.Idle
|
||||
eta = 0.0
|
||||
}
|
||||
static member random =
|
||||
let p = Random().Next(0,100)
|
||||
let s =
|
||||
if Random().Next(0,100) > 70 then
|
||||
SimState.Running
|
||||
else
|
||||
SimState.Idle
|
||||
{
|
||||
progress = p
|
||||
state = s
|
||||
eta = 0.0
|
||||
}
|
||||
and private SimState =
|
||||
| Idle
|
||||
| Running
|
||||
| Completed
|
||||
|
||||
let private siteDataToTableItems (site: Fiskeridir.SiteData) : TableItem =
|
||||
{
|
||||
id = site.SiteNr
|
||||
name = site.Name
|
||||
species = site.SpeciesType
|
||||
latitude = site.Latitude
|
||||
longitude = site.Longitude
|
||||
capacity = site.Capacity
|
||||
municipality = site.Placement.MunicipalityName
|
||||
prodAreaName = site.Placement.ProdAreaName
|
||||
prodAreaCode = site.Placement.ProdAreaCode |> tryParseInt |> Option.defaultValue 0
|
||||
liceCoverage = SimCoverage.random
|
||||
wcCoverage = SimCoverage.random
|
||||
}
|
||||
|
||||
let private fetchProductionAreaInfo onFetch (poOpt: int option) =
|
||||
promise {
|
||||
match poOpt with
|
||||
| Some po ->
|
||||
let! data = Fiskeridir.fetchInfoPO po
|
||||
data |> onFetch
|
||||
| None ->
|
||||
Fiskeridir.ProductionAreaData.empty
|
||||
|> onFetch
|
||||
}
|
||||
|
||||
let private fetchAquacultureSites onFetch (poOpt: int option) =
|
||||
promise {
|
||||
match poOpt with
|
||||
| Some po ->
|
||||
let! sites =
|
||||
[| for i in 0..9 do
|
||||
// the api accepts only 100 sites at a time, so batching up the requests
|
||||
// don't know how many sites there are, so fetching up to 1000...
|
||||
let range = (i*100, i*100 + 99)
|
||||
Fiskeridir.fetchSitesPO range po
|
||||
|]
|
||||
|> Array.map (Promise.map (Array.map siteDataToTableItems))
|
||||
|> Promise.all
|
||||
|> Promise.map Array.concat
|
||||
|
||||
sites
|
||||
|> onFetch
|
||||
| None -> ()
|
||||
}
|
||||
|
||||
// [<LitElement("archive-name")>]
|
||||
// let archiveName () =
|
||||
// let host, props =
|
||||
// LitElement.init (fun cfg ->
|
||||
// private cfg.useShadowDom <- true
|
||||
// cfg.props <- {|
|
||||
// item = Prop.Of(defaultValue = TableItem.empty , attribute = "")
|
||||
// |}
|
||||
// cfg.styles <- [ unsafeCSS "" ]
|
||||
// )
|
||||
//
|
||||
// let item = props.item.Value
|
||||
// let onChange (name: string) = host.dispatchCustomEvent("rename", name)
|
||||
//
|
||||
// if item.editing then
|
||||
// html $"""
|
||||
// <sp-textfield id="a-{item.id}" value="{item.name}" @change={EvVal(onChange)}></sp-textfield>
|
||||
// """
|
||||
// else
|
||||
// html $"{item.name}"
|
||||
//
|
||||
[<HookComponent>]
|
||||
let localitiesDialog
|
||||
(arg: {|
|
||||
onClose: unit -> unit
|
||||
prodArea: int option
|
||||
|}) =
|
||||
let items, setItems = Hook.useState<TableItem array> [||]
|
||||
let modelData, setModelData = Hook.useState Fiskeridir.ProductionAreaData.empty
|
||||
|
||||
// let updateItems = Array.map siteDataToTableItems >> setItems
|
||||
let appendItems = Array.append items >> setItems
|
||||
|
||||
Hook.useEffectOnce (Utils.handleKeyPress "Escape" arg.onClose)
|
||||
// Hook.useEffectOnChange (arg.localities, updateItems)
|
||||
|
||||
Hook.useEffectOnChange(items, fun _ ->
|
||||
let table = document.getElementById "localities-table"
|
||||
table?selected <-
|
||||
items
|
||||
|> Array.choose (fun item ->
|
||||
Some item.id)
|
||||
table?items <- items)
|
||||
|
||||
|
||||
// NOTE(simkir): To enable a virtualized table, we do not create the rows by iterating over drifters, we instead
|
||||
// programatically add the rows to the table. In this useEffect, we wait for the table to be created, and then we
|
||||
// add all the items.
|
||||
// sp-table api: https://opensource.adobe.com/spectrum-web-components/components/table/api/
|
||||
Hook.useEffectOnce (fun () ->
|
||||
arg.prodArea
|
||||
|> fetchProductionAreaInfo setModelData
|
||||
|> Promise.start
|
||||
|
||||
arg.prodArea
|
||||
|> fetchAquacultureSites setItems
|
||||
|> Promise.start
|
||||
|
||||
let table = document.getElementById "localities-table"
|
||||
table?selected <- items
|
||||
|
||||
// NOTE: These are members of the spectrum table that we get
|
||||
// TODO: Make a small class for type safety
|
||||
table?items <- items
|
||||
// NOTE: Function to get an id for each row
|
||||
table?itemValue <- (fun (item : TableItem) -> $"{item.id}")
|
||||
// NOTE: Function to render each row. Creating the cell elements that corresponds to the header. Calls
|
||||
// this function on each item that we have sent into it.
|
||||
table?renderItem <- (fun (item: TableItem) ->
|
||||
let color (coverage: SimCoverage) =
|
||||
match coverage.state with
|
||||
| Idle -> "rgb(0,0,0,0.0)"
|
||||
| Running -> "rgb(30,144,255,0.7)"
|
||||
| Completed -> "rgb(0,128,0,0.4)"
|
||||
html $"""
|
||||
<sp-table-cell style="max-width: 100px"><div style="padding-top: 5px;">{item.id}</div></sp-table-cell>
|
||||
<sp-table-cell style="max-width: 200px"><div style="padding-top: 5px;">{item.name}</div></sp-table-cell>
|
||||
<sp-table-cell style="max-width: 130px"><div style="padding-top: 5px;">{item.latitude |> sprintf "%.6f"}</div></sp-table-cell>
|
||||
<sp-table-cell style="max-width: 130px"><div style="padding-top: 5px;">{item.longitude |> sprintf "%.6f"}</div></sp-table-cell>
|
||||
<sp-table-cell style="max-width: 130px"><div style="padding-top: 5px;">{item.capacity |> sprintf "%.0f"}</div></sp-table-cell>
|
||||
<sp-table-cell style="max-width: 200px"><div style="padding-top: 5px;">{item.municipality}</div></sp-table-cell>
|
||||
<sp-table-cell style="max-width: 250px">
|
||||
<div
|
||||
style="
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: space-around;
|
||||
background-color: {color item.liceCoverage};
|
||||
">
|
||||
<sp-progress-bar over-background side-label label="Lice" progress={item.liceCoverage.progress}></sp-progress-bar>
|
||||
</div>
|
||||
</sp-table-cell>
|
||||
<sp-table-cell style="max-width: 250px">
|
||||
<div
|
||||
style="
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: space-around;
|
||||
background-color: {color item.wcCoverage};
|
||||
">
|
||||
<sp-progress-bar side-label label="WC" progress={item.wcCoverage.progress}></sp-progress-bar>
|
||||
</div>
|
||||
</sp-table-cell>
|
||||
""")
|
||||
|
||||
// NOTE: The table fires a "sorted" event when you click on the table headers
|
||||
table.addEventListener("sorted", fun ev ->
|
||||
// NOTE: The event has a detail member with these two members
|
||||
let sortDir: string = ev?detail?sortDirection
|
||||
let sortKey: string = ev?detail?sortKey // This comes from the sort-key given to the headers below
|
||||
let sortFn = if sortDir = "asc" then Array.sortBy else Array.sortByDescending
|
||||
table?items
|
||||
|> sortFn (fun item ->
|
||||
// NOTE(simkir): WARNING! Hack to access an objects members :) Everything is an object in
|
||||
// JS, right!?
|
||||
JS.expr_js $"{item}[{sortKey}]")
|
||||
|> setItems))
|
||||
|
||||
|
||||
// table.addEventListener("change", fun ev ->
|
||||
// const selected = event.target.nextElementSibling
|
||||
// selected.textContent = "Selected: ${JSON.stringify(event.target.selected.Length";
|
||||
// ))
|
||||
|
||||
// let fetchSitesPO (poOpt: int option) _ =
|
||||
// poOpt
|
||||
// |> Option.map (fun po ->
|
||||
// promise {
|
||||
// let! sites = Fiskeridir.fetchSitesPO po
|
||||
// sites
|
||||
// |> Array.map siteDataToTableItems
|
||||
// |> setItems
|
||||
// } |> Promise.start)
|
||||
// |> Option.defaultValue()
|
||||
|
||||
// let fetchSitesButton =
|
||||
// html $"""
|
||||
// <sp-field-group horizontal>
|
||||
// <sp-action-button
|
||||
// ?disabled={arg.prodArea.IsNone}
|
||||
// @click={Ev(fetchSitesPO arg.prodArea)}>
|
||||
// <sp-icon-location slot="icon"></sp-icon-location>
|
||||
// Fetch sites
|
||||
// </sp-action-button>
|
||||
// </sp-field-group>
|
||||
// """
|
||||
//
|
||||
let setupSimButton =
|
||||
html $"""
|
||||
<sp-action-menu size="m">
|
||||
<sp-icon-social-network slot="icon"></sp-icon-social-network>
|
||||
<span slot="label">Network analysis</span>
|
||||
<sp-menu-item>
|
||||
Lice
|
||||
</sp-menu-item>
|
||||
<sp-menu-item>
|
||||
Water Contact
|
||||
</sp-menu-item>
|
||||
</sp-action-menu>
|
||||
"""
|
||||
|
||||
let table =
|
||||
html $"""
|
||||
<sp-table
|
||||
id="localities-table"
|
||||
size="s"
|
||||
selects="multiple"
|
||||
scroller="true"
|
||||
style="height: 92%%; "
|
||||
@change={Ev(fun _ -> console.log $"CHANGE TABLE : {items.Length}")}
|
||||
>
|
||||
<sp-table-head>
|
||||
<sp-table-head-cell sortable sort-key="id" style="max-width: 100px">
|
||||
SiteId
|
||||
</sp-table-head-cell>
|
||||
<sp-table-head-cell sortable sort-key="name" style="max-width: 200px">
|
||||
Name
|
||||
</sp-table-head-cell>
|
||||
<sp-table-head-cell sortable sort-key="latitude" style="max-width: 130px">
|
||||
Latitude
|
||||
</sp-table-head-cell>
|
||||
<sp-table-head-cell sortable sort-key="longitude" style="max-width: 130px">
|
||||
Longitude
|
||||
</sp-table-head-cell>
|
||||
<sp-table-head-cell sortable sort-key="capacity" style="max-width: 130px">
|
||||
Capacity
|
||||
</sp-table-head-cell>
|
||||
<sp-table-head-cell sortable sort-key="municipality" style="max-width: 200px">
|
||||
Municipality
|
||||
</sp-table-head-cell>
|
||||
<sp-table-head-cell sortable sort-key="coverage" style="max-width: 500px">
|
||||
Simulation Coverage
|
||||
</sp-table-head-cell>
|
||||
</sp-table-head>
|
||||
</sp-table>
|
||||
"""
|
||||
|
||||
html $"""
|
||||
<div class="archive-dialog">
|
||||
<sp-underlay ?open={true} @click={Ev(ignore >> arg.onClose)}></sp-underlay>
|
||||
<sp-dialog size="l" no-divider dismissable @close={Ev(fun _ -> arg.onClose ())}>
|
||||
<h1 slot="heading">{modelData.ToLabel()}</h1>
|
||||
{table}
|
||||
<div style="padding-top: 10px">
|
||||
<sp-action-group horizontal>
|
||||
{setupSimButton}
|
||||
</sp-action-group>
|
||||
</div>
|
||||
</sp-dialog>
|
||||
</div>
|
||||
"""
|
||||
// {fetchSitesButton}
|
||||
|
||||
@@ -8,6 +8,7 @@ open Fable.Core.JsInterop
|
||||
open Fable.OpenLayers
|
||||
open Lit
|
||||
open Lit.Elmish
|
||||
open System.Text.RegularExpressions
|
||||
|
||||
open Atlantis.Shared
|
||||
open Atlantis.Shared.Notification
|
||||
@@ -1830,6 +1831,7 @@ let MapAppElement () =
|
||||
// TODO: I don't know why I'm getting errors here in rider VVVVVVV
|
||||
let (model: Model), (dispatch: Msg -> unit) = Hook.useElmish(program)
|
||||
let archivesOpen, setArchivesOpen = Hook.useState false
|
||||
let localitiesOpen, setLocalitiesOpen = Hook.useState false
|
||||
let inboxOpen, setInboxOpen = Hook.useState false
|
||||
// let (simPolicies: DriftersPolicy[]), setSimPolicies = Hook.useState [||]
|
||||
|
||||
@@ -2039,6 +2041,21 @@ let MapAppElement () =
|
||||
selectArchive = SetSelectedDrifter >> dispatch
|
||||
|}
|
||||
|
||||
let tryGetPoNumber (name: string) : int option =
|
||||
name.Split('-')
|
||||
|> Array.head
|
||||
|> trySplitPrefixAndInt
|
||||
|> Option.bind (function
|
||||
| "PO", number -> Some number
|
||||
| _ -> None)
|
||||
|
||||
let localitiesDialogArgs = {|
|
||||
onClose = fun () -> setLocalitiesOpen false
|
||||
prodArea =
|
||||
model.archive.name
|
||||
|> tryGetPoNumber
|
||||
|}
|
||||
|
||||
let selectInboxItem (id, type': MessageType) =
|
||||
Hub.Action.Inbox (Hub.InboxMsg.MarkRead id) |> (HubMsg >> dispatch)
|
||||
match type' with
|
||||
@@ -2123,7 +2140,7 @@ let MapAppElement () =
|
||||
html
|
||||
$"""
|
||||
<sp-theme system="spectrum-two" scale="medium" color="light" style="height: inherit">
|
||||
{Navigation.toolboxNav setArchivesOpen setInboxOpen model dispatch}
|
||||
{Navigation.toolboxNav setLocalitiesOpen setArchivesOpen setInboxOpen model dispatch}
|
||||
<div class="box" style="padding-left: 46px; height=100%%">
|
||||
<sp-split-view
|
||||
primary-size="350px"
|
||||
@@ -2148,6 +2165,7 @@ let MapAppElement () =
|
||||
</sp-split-view>
|
||||
{notificationToast model.notification}
|
||||
{if archivesOpen then ArchiveDialog.archiveDialog archiveDialogArgs else Lit.nothing}
|
||||
{if localitiesOpen then LocalitiesDialog.localitiesDialog localitiesDialogArgs else Lit.nothing}
|
||||
{if inboxOpen then Inbox.inboxDialog inboxArgs else Lit.nothing}
|
||||
<div style="display: none">
|
||||
{aquaculturePopup}
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
<Compile Include="Stats.fs" />
|
||||
<Compile Include="DriftersPlots.fs" />
|
||||
<Compile Include="ArchiveDialog.fs" />
|
||||
<Compile Include="LocalitiesDialog.fs" />
|
||||
<Compile Include="Inbox.fs" />
|
||||
<Compile Include="Navigation.fs" />
|
||||
<Compile Include="Mapster.fs" />
|
||||
|
||||
@@ -729,7 +729,7 @@ let sideNav (dispatch: Msg -> unit) (model: Model) (navMode: SideNavMode) =
|
||||
</div>
|
||||
"""
|
||||
|
||||
let toolboxNav setArchivesOpen setInboxOpen model dispatch =
|
||||
let toolboxNav setLocalitiesOpen setArchivesOpen setInboxOpen model dispatch =
|
||||
let statsDisabled = false
|
||||
let canSubmit = true
|
||||
let isActive x = if model.sideNavMode = x then "active" else ""
|
||||
@@ -794,6 +794,7 @@ let toolboxNav setArchivesOpen setInboxOpen model dispatch =
|
||||
"""
|
||||
else Lit.nothing
|
||||
|
||||
let openLocalities ev = setLocalitiesOpen true
|
||||
let openArchives ev = setArchivesOpen true
|
||||
let openInbox ev = setInboxOpen true
|
||||
|
||||
@@ -851,6 +852,17 @@ let toolboxNav setArchivesOpen setInboxOpen model dispatch =
|
||||
{cropButton}
|
||||
</div>
|
||||
<div class="toolboxBottom">
|
||||
<overlay-trigger id="trigger" placement="right" offset="6">
|
||||
<sp-tooltip slot="hover-content">Akvakulturregisteret</sp-tooltip>
|
||||
<div slot="trigger" class="toolbox-control">
|
||||
<div
|
||||
class="toolboxIcon"
|
||||
@click={Ev(openLocalities)}
|
||||
>
|
||||
<i class="fas fa-fish"></i>
|
||||
</div>
|
||||
</div>
|
||||
</overlay-trigger>
|
||||
<overlay-trigger id="trigger" placement="right" offset="6">
|
||||
<sp-tooltip slot="hover-content">Inbox</sp-tooltip>
|
||||
<div slot="trigger" class="toolbox-control">
|
||||
|
||||
Reference in New Issue
Block a user