🍴 Meu Garfo é uma visualização em grafo dos CNPJs cuducos.tngl.io/meu-garfo
1

Configure Feed

Select the types of activity you want to include in your feed.

Adapts client to the new backend API

+396 -336
+14 -50
src/Api.elm
··· 1 - module Api exposing (fetchCompanyName, queryEntity, responseDecoder) 1 + module Api exposing (fetchCompanyName, queryEntity) 2 2 3 3 import Http 4 4 import Json.Decode as Decode exposing (Decoder, field, string) ··· 54 54 else 55 55 graphApi ++ "/" 56 56 57 - ( path, id ) = 57 + ( id, url ) = 58 58 case qType of 59 - CompanyCnpj cnpj -> 60 - ( "qsa/", cnpj ) 61 - 62 - CompanyAsPartner cnpj -> 63 - ( "cnpjs/", cnpj ) 59 + EntityQuery term -> 60 + ( term, base ++ "relacoes/" ++ term ) 64 61 65 - PersonUid uid -> 66 - ( "cnpjs/", uid ) 67 - 68 - url = 69 - base ++ path ++ id 62 + ConnectionQuery id1 id2 -> 63 + ( id1 ++ ";" ++ id2, base ++ "conexao/" ++ id1 ++ "/" ++ id2 ) 70 64 in 71 65 Http.get 72 66 { url = url ··· 102 96 Err (HttpError (Http.BadStatus meta.statusCode)) 103 97 104 98 Http.GoodStatus_ _ body -> 105 - case Decode.decodeString responseDecoder body of 99 + case Decode.decodeString (Decode.list relationDecoder) body of 106 100 Ok value -> 107 101 Ok value 108 102 ··· 110 104 Err (HttpError (Http.BadBody (Decode.errorToString err))) 111 105 112 106 113 - responseDecoder : Decoder ApiResponse 114 - responseDecoder = 115 - Decode.oneOf 116 - [ Decode.map CompanyResponse companyDecoder 117 - , Decode.map PartnerResponse partnerDecoder 118 - ] 119 - 120 - 121 - companyDecoder : Decoder CompanyData 122 - companyDecoder = 123 - Decode.map3 CompanyData 124 - (field "company_id" string) 125 - (field "name" string) 126 - (field "partners" (Decode.list partnerRefDecoder)) 127 - 128 - 129 - partnerRefDecoder : Decoder PartnerRef 130 - partnerRefDecoder = 131 - Decode.map3 PartnerRef 132 - (field "partner_id" (Decode.oneOf [ string, Decode.map String.fromInt Decode.int ])) 133 - (Decode.maybe (field "name" string)) 134 - (Decode.maybe (field "cpf" string)) 135 - 136 - 137 - partnerDecoder : Decoder PartnerData 138 - partnerDecoder = 139 - Decode.map4 PartnerData 140 - (Decode.oneOf [ field "partner_id" string, field "partnerd_id" string, field "partner_id" (Decode.map String.fromInt Decode.int) ]) 141 - (Decode.maybe (field "name" string)) 142 - (Decode.maybe (field "cpf" string)) 143 - (field "companies" (Decode.list companyRefDecoder)) 144 - 145 - 146 - companyRefDecoder : Decoder CompanyRef 147 - companyRefDecoder = 148 - Decode.map2 CompanyRef 107 + relationDecoder : Decoder Relation 108 + relationDecoder = 109 + Decode.map5 Relation 149 110 (field "cnpj" string) 150 - (field "name" string) 111 + (field "razao_social" string) 112 + (field "id" string) 113 + (Decode.maybe (field "nome" string)) 114 + (Decode.maybe (field "cpf" string))
+245 -233
src/Main.elm
··· 41 41 let 42 42 initialModel = 43 43 { input = "" 44 + , connectionInput1 = "" 45 + , connectionInput2 = "" 46 + , activeTab = CnpjTab 44 47 , nodes = Dict.empty 45 48 , edges = Set.empty 46 49 , visited = Set.empty ··· 64 67 65 68 ( finalModel, initialCmd ) = 66 69 case parseUrl url of 67 - Just cnpj -> 70 + CnpjRoute cnpj -> 68 71 let 69 72 seeded = 70 73 { initialModel | input = Format.mask cnpj } ··· 72 75 in 73 76 triggerNextQuery seeded 74 77 75 - Nothing -> 78 + ConnectionRoute id1 id2 -> 79 + let 80 + seeded = 81 + { initialModel 82 + | connectionInput1 = Format.mask id1 83 + , connectionInput2 = Format.mask id2 84 + , activeTab = ConnectionTab 85 + } 86 + |> enqueueQueries [ QueryRequest (id1 ++ ";" ++ id2) 0 (ConnectionQuery id1 id2) ] 87 + in 88 + triggerNextQuery seeded 89 + 90 + HomeRoute -> 76 91 ( initialModel, Cmd.none ) 77 92 in 78 93 ( finalModel ··· 87 102 update msg model = 88 103 case msg of 89 104 UpdateInput s -> 90 - ( { model | input = Format.maskCnpjInput model.input s }, Cmd.none ) 105 + if String.length (String.filter Char.isAlphaNum s) > 14 then 106 + ( { model | input = String.left 32 s }, Cmd.none ) 91 107 92 - Search -> 93 - let 94 - unmasked = 95 - String.filter Char.isAlphaNum model.input 108 + else 109 + ( { model | input = Format.maskCnpjInput model.input s }, Cmd.none ) 96 110 97 - currentCnpj = 98 - parseUrl model.url 99 - in 100 - if String.isEmpty unmasked then 101 - ( { model | error = Just "Por favor, digite um CNPJ" }, Cmd.none ) 111 + UpdateConnectionInput1 s -> 112 + if String.length (String.filter Char.isAlphaNum s) > 14 then 113 + ( { model | connectionInput1 = String.left 32 s }, Cmd.none ) 114 + 115 + else 116 + ( { model | connectionInput1 = Format.maskCnpjInput model.connectionInput1 s }, Cmd.none ) 102 117 103 - else if currentCnpj == Just unmasked then 104 - ( model, Cmd.none ) 118 + UpdateConnectionInput2 s -> 119 + if String.length (String.filter Char.isAlphaNum s) > 14 then 120 + ( { model | connectionInput2 = String.left 32 s }, Cmd.none ) 105 121 106 122 else 107 - ( { model 108 - | nodes = Dict.empty 109 - , edges = Set.empty 110 - , visited = Set.empty 111 - , pending = Set.empty 112 - , queryQueue = [] 113 - , currentQuery = Nothing 114 - , error = Nothing 115 - , simulation = Graph.initSimulation model.width model.height [] 116 - , pan = { x = 0, y = 0 } 117 - , zoom = 1.0 118 - } 119 - , Nav.pushUrl model.navKey ("#" ++ unmasked) 120 - ) 123 + ( { model | connectionInput2 = Format.maskCnpjInput model.connectionInput2 s }, Cmd.none ) 124 + 125 + SwitchTab tab -> 126 + ( { model | activeTab = tab }, Cmd.none ) 127 + 128 + Search -> 129 + case model.activeTab of 130 + CnpjTab -> 131 + let 132 + unmasked = 133 + String.filter Char.isAlphaNum model.input 134 + 135 + currentRoute = 136 + parseUrl model.url 137 + in 138 + if String.isEmpty unmasked then 139 + ( { model | error = Just "Por favor, digite um CNPJ" }, Cmd.none ) 140 + 141 + else if currentRoute == CnpjRoute unmasked then 142 + ( model, Cmd.none ) 143 + 144 + else 145 + ( { model 146 + | nodes = Dict.empty 147 + , edges = Set.empty 148 + , visited = Set.empty 149 + , pending = Set.empty 150 + , queryQueue = [] 151 + , currentQuery = Nothing 152 + , error = Nothing 153 + , simulation = Graph.initSimulation model.width model.height [] 154 + , pan = { x = 0, y = 0 } 155 + , zoom = 1.0 156 + } 157 + , Nav.pushUrl model.navKey ("#/grafo/" ++ unmasked) 158 + ) 159 + 160 + ConnectionTab -> 161 + let 162 + id1 = 163 + String.filter Char.isAlphaNum model.connectionInput1 164 + 165 + id2 = 166 + String.filter Char.isAlphaNum model.connectionInput2 167 + 168 + currentRoute = 169 + parseUrl model.url 170 + in 171 + if String.isEmpty id1 || String.isEmpty id2 then 172 + ( { model | error = Just "Por favor, digite ambos os campos" }, Cmd.none ) 173 + 174 + else if currentRoute == ConnectionRoute id1 id2 then 175 + ( model, Cmd.none ) 176 + 177 + else 178 + ( { model 179 + | nodes = Dict.empty 180 + , edges = Set.empty 181 + , visited = Set.empty 182 + , pending = Set.empty 183 + , queryQueue = [] 184 + , currentQuery = Nothing 185 + , error = Nothing 186 + , simulation = Graph.initSimulation model.width model.height [] 187 + , pan = { x = 0, y = 0 } 188 + , zoom = 1.0 189 + } 190 + , Nav.pushUrl model.navKey ("#/conexao/" ++ id1 ++ "/" ++ id2) 191 + ) 121 192 122 193 NodeClicked id -> 123 194 case Dict.get id model.nodes of 124 195 Just node -> 125 196 let 126 197 queries = 127 - case node.entity of 128 - Company _ cnpj -> 129 - queriesFor cnpj True node.depth 130 - 131 - Person _ _ -> 132 - [ QueryRequest id node.depth (PersonUid id) ] 198 + queriesFor id False node.depth 133 199 in 134 200 triggerNextQuery (enqueueQueries queries model) 135 201 ··· 140 206 let 141 207 key = 142 208 Maybe.map (\q -> queryKey q.queryType) model.currentQuery 143 - |> Maybe.withDefault ( id, "qsa" ) 209 + |> Maybe.withDefault ( id, "relacoes" ) 144 210 145 211 baseModel = 146 212 { model ··· 156 222 Ok response -> 157 223 let 158 224 ( updatedModel, newQueue, extraCmd ) = 159 - processResponse response depth { baseModel | error = Nothing } 225 + processResponse id response depth { baseModel | error = Nothing } 160 226 161 227 ( nextModel, nextCmd ) = 162 228 triggerNextQuery ··· 267 333 268 334 UrlChanged url -> 269 335 case parseUrl url of 270 - Just cnpj -> 336 + CnpjRoute cnpj -> 271 337 let 272 338 isSameCnpj = 273 339 cnpj == String.filter (\c -> Char.isAlphaNum c || c == '*') model.input ··· 288 354 , currentQuery = Nothing 289 355 , simulation = Graph.initSimulation model.width model.height [] 290 356 , url = url 357 + , activeTab = CnpjTab 291 358 } 292 359 in 293 360 triggerNextQuery (enqueueQueries (queriesFor cnpj True 0) reset) 294 361 295 - Nothing -> 362 + ConnectionRoute id1 id2 -> 363 + let 364 + isSame = 365 + id1 == String.filter Char.isAlphaNum model.connectionInput1 && id2 == String.filter Char.isAlphaNum model.connectionInput2 366 + in 367 + if isSame && not (Dict.isEmpty model.nodes) then 368 + ( model, Cmd.none ) 369 + 370 + else 371 + let 372 + reset = 373 + { model 374 + | connectionInput1 = Format.mask id1 375 + , connectionInput2 = Format.mask id2 376 + , nodes = Dict.empty 377 + , edges = Set.empty 378 + , visited = Set.empty 379 + , pending = Set.empty 380 + , queryQueue = [] 381 + , currentQuery = Nothing 382 + , simulation = Graph.initSimulation model.width model.height [] 383 + , url = url 384 + , activeTab = ConnectionTab 385 + } 386 + in 387 + triggerNextQuery (enqueueQueries [ QueryRequest (id1 ++ ";" ++ id2) 0 (ConnectionQuery id1 id2) ] reset) 388 + 389 + HomeRoute -> 296 390 ( { model | url = url }, Cmd.none ) 297 391 298 392 299 - parseUrl : Url -> Maybe String 393 + type Route 394 + = CnpjRoute String 395 + | ConnectionRoute String String 396 + | HomeRoute 397 + 398 + 399 + parseUrl : Url -> Route 300 400 parseUrl url = 301 401 case url.fragment of 302 402 Just frag -> 303 403 let 304 - cnpj = 305 - String.split ";" frag 306 - |> List.head 307 - |> Maybe.withDefault "" 404 + cleanFrag = 405 + if String.startsWith "/" frag then 406 + String.dropLeft 1 frag 407 + 408 + else 409 + frag 410 + 411 + parts = 412 + String.split "/" cleanFrag 308 413 in 309 - if String.isEmpty cnpj then 310 - Nothing 414 + case parts of 415 + [ "conexao", id1, id2 ] -> 416 + ConnectionRoute id1 id2 417 + 418 + [ "grafo", id ] -> 419 + CnpjRoute id 420 + 421 + _ -> 422 + let 423 + semiParts = 424 + String.split ";" frag 425 + in 426 + case semiParts of 427 + [ id1, id2 ] -> 428 + ConnectionRoute id1 id2 429 + 430 + [ id ] -> 431 + if String.isEmpty id then 432 + HomeRoute 311 433 312 - else 313 - Just cnpj 434 + else 435 + CnpjRoute id 436 + 437 + _ -> 438 + HomeRoute 314 439 315 440 Nothing -> 316 - Nothing 441 + HomeRoute 317 442 318 443 319 444 triggerNextQuery : Model -> ( Model, Cmd Msg ) ··· 337 462 ( model, Cmd.none ) 338 463 339 464 340 - processResponse : ApiResponse -> Int -> Model -> ( Model, List QueryRequest, Cmd Msg ) 341 - processResponse response depth model = 342 - case response of 343 - CompanyResponse data -> 465 + processResponse : String -> ApiResponse -> Int -> Model -> ( Model, List QueryRequest, Cmd Msg ) 466 + processResponse queriedId relations depth model = 467 + let 468 + processRelation rel ( accModel, accCmds ) = 344 469 let 345 - ( nx, ny ) = 346 - nodeOffset data.id model.width model.height 470 + ( c_nx, c_ny ) = 471 + if Dict.member rel.cnpj accModel.nodes then 472 + ( 0, 0 ) 473 + 474 + else 475 + spawnNear rel.partnerId rel.cnpj accModel 347 476 348 - newNode = 349 - case Dict.get data.id model.nodes of 477 + companyNode = 478 + case Dict.get rel.cnpj accModel.nodes of 350 479 Just existing -> 351 - { existing | entity = Company data.name data.id } 480 + { existing | entity = Company rel.razaoSocial rel.cnpj } 352 481 353 482 Nothing -> 354 - { id = data.id, entity = Company data.name data.id, depth = depth, x = nx, y = ny, vx = 0, vy = 0, error = Nothing } 483 + { id = rel.cnpj, entity = Company rel.razaoSocial rel.cnpj, depth = depth + 1, x = c_nx, y = c_ny, vx = 0, vy = 0, error = Nothing } 355 484 356 - updatedNodes = 357 - Dict.insert data.id newNode model.nodes 358 - 359 - ( modelWithPartners, _, cmd ) = 360 - List.foldl (processPartnerEdge data.id depth) ( { model | nodes = updatedNodes }, [], Cmd.none ) data.partners 361 - in 362 - ( modelWithPartners 363 - , enqueueChildren data.id depth modelWithPartners 364 - , cmd 365 - ) 485 + ( p_nx, p_ny ) = 486 + if Dict.member rel.partnerId accModel.nodes then 487 + ( 0, 0 ) 366 488 367 - PartnerResponse data -> 368 - let 369 - isCompany = 370 - isCnpj data.id 489 + else 490 + spawnNear rel.cnpj rel.partnerId accModel 371 491 372 - ( nx, ny ) = 373 - nodeOffset data.id model.width model.height 492 + partnerName = 493 + Maybe.withDefault rel.partnerId rel.partnerName 374 494 375 - newNode = 376 - case Dict.get data.id model.nodes of 495 + partnerNode = 496 + case Dict.get rel.partnerId accModel.nodes of 377 497 Just existing -> 378 - if isCompany then 379 - existing 380 - 381 - else 382 - let 383 - name = 384 - Maybe.withDefault data.id data.name 385 - in 386 - case existing.entity of 387 - Person oldName oldCpf -> 388 - { existing 389 - | entity = 390 - Person name 391 - (if data.cpf /= Nothing then 392 - data.cpf 498 + case existing.entity of 499 + Person _ _ -> 500 + { existing | entity = Person partnerName rel.partnerCpf } 393 501 394 - else 395 - oldCpf 396 - ) 397 - } 398 - 399 - _ -> 400 - { existing | entity = Person name data.cpf } 502 + Company _ _ -> 503 + existing 401 504 402 505 Nothing -> 403 - if isCompany then 404 - { id = data.id, entity = Company (Maybe.withDefault data.id data.name) data.id, depth = depth, x = nx, y = ny, vx = 0, vy = 0, error = Nothing } 506 + let 507 + entity = 508 + if isCnpj rel.partnerId then 509 + Company partnerName rel.partnerId 405 510 406 - else 407 - let 408 - name = 409 - Maybe.withDefault data.id data.name 410 - in 411 - { id = data.id, entity = Person name data.cpf, depth = depth, x = nx, y = ny, vx = 0, vy = 0, error = Nothing } 511 + else 512 + Person partnerName rel.partnerCpf 513 + in 514 + { id = rel.partnerId, entity = entity, depth = depth + 1, x = p_nx, y = p_ny, vx = 0, vy = 0, error = Nothing } 515 + 516 + edge = 517 + ( rel.cnpj, rel.partnerId ) 412 518 413 519 updatedNodes = 414 - Dict.insert data.id newNode model.nodes 520 + accModel.nodes 521 + |> Dict.insert rel.cnpj companyNode 522 + |> Dict.insert rel.partnerId partnerNode 415 523 416 - ( modelWithCompanies, _, cmd ) = 417 - List.foldl (processCompanyEdge data.id depth) ( { model | nodes = updatedNodes }, [], Cmd.none ) data.companies 524 + updatedEdges = 525 + Set.insert edge accModel.edges 418 526 in 419 - ( modelWithCompanies 420 - , enqueueChildren data.id depth modelWithCompanies 421 - , cmd 422 - ) 423 - 424 - 425 - processPartnerEdge : String -> Int -> PartnerRef -> ( Model, List QueryRequest, Cmd Msg ) -> ( Model, List QueryRequest, Cmd Msg ) 426 - processPartnerEdge companyId depth partner ( model, _, cmds ) = 427 - let 428 - edge = 429 - ( companyId, partner.id ) 430 - 431 - updatedEdges = 432 - Set.insert edge model.edges 433 - 434 - name = 435 - Maybe.withDefault partner.id partner.name 436 - 437 - isPartnerCnpj = 438 - isCnpj partner.id 527 + ( { accModel | nodes = updatedNodes, edges = updatedEdges }, accCmds ) 439 528 440 - entity = 441 - if isPartnerCnpj then 442 - Company name partner.id 529 + ( modelWithNodes, cmds ) = 530 + List.foldl processRelation ( model, Cmd.none ) relations 443 531 444 - else 445 - Person name partner.cpf 446 - 447 - ( nx, ny ) = 448 - spawnNear companyId partner.id model 449 - 450 - targetNode = 451 - case Dict.get partner.id model.nodes of 452 - Just existing -> 453 - if partner.name /= Nothing || partner.cpf /= Nothing then 454 - case existing.entity of 455 - Person oldName oldCpf -> 456 - { existing 457 - | entity = 458 - Person 459 - (if partner.name /= Nothing then 460 - name 461 - 462 - else 463 - oldName 464 - ) 465 - (if partner.cpf /= Nothing then 466 - partner.cpf 467 - 468 - else 469 - oldCpf 470 - ) 471 - } 472 - 473 - _ -> 474 - { existing | entity = entity } 532 + finalNodes = 533 + let 534 + idsToUpdate = 535 + if String.contains ";" queriedId then 536 + String.split ";" queriedId 475 537 476 538 else 477 - existing 478 - 479 - Nothing -> 480 - { id = partner.id, entity = entity, depth = depth + 1, x = nx, y = ny, vx = 0, vy = 0, error = Nothing } 481 - 482 - newModel = 483 - { model | edges = updatedEdges, nodes = Dict.insert partner.id targetNode model.nodes } 484 - 485 - newCmd = 486 - if isPartnerCnpj && partner.name == Nothing then 487 - Cmd.batch [ cmds, Api.fetchCompanyName model.jsonApi partner.id ] 488 - 489 - else 490 - cmds 491 - in 492 - ( newModel, [], newCmd ) 493 - 494 - 495 - processCompanyEdge : String -> Int -> CompanyRef -> ( Model, List QueryRequest, Cmd Msg ) -> ( Model, List QueryRequest, Cmd Msg ) 496 - processCompanyEdge partnerId depth company ( model, _, cmds ) = 497 - let 498 - edge = 499 - ( company.id, partnerId ) 539 + [ queriedId ] 500 540 501 - updatedEdges = 502 - Set.insert edge model.edges 503 - 504 - ( nx, ny ) = 505 - spawnNear partnerId company.id model 541 + updateDepth id dict = 542 + Dict.update id 543 + (Maybe.map (\n -> if n.depth > depth then { n | depth = depth } else n)) 544 + dict 545 + in 546 + List.foldl updateDepth modelWithNodes.nodes idsToUpdate 547 + in 548 + ( { modelWithNodes | nodes = finalNodes } 506 549 507 - targetNode = 508 - case Dict.get company.id model.nodes of 509 - Just existing -> 510 - { existing | entity = Company company.name company.id } 511 - 512 - Nothing -> 513 - { id = company.id, entity = Company company.name company.id, depth = depth + 1, x = nx, y = ny, vx = 0, vy = 0, error = Nothing } 514 - 515 - newModel = 516 - { model | edges = updatedEdges, nodes = Dict.insert company.id targetNode model.nodes } 517 - in 518 - ( newModel, [], cmds ) 550 + , enqueueChildren queriedId depth modelWithNodes 551 + , cmds 552 + ) 519 553 520 554 521 555 isCnpj : String -> Bool ··· 536 570 queryKey : QueryType -> QueryKey 537 571 queryKey qType = 538 572 case qType of 539 - CompanyCnpj cnpj -> 540 - ( cnpj, "qsa" ) 573 + EntityQuery id -> 574 + ( id, "relacoes" ) 541 575 542 - CompanyAsPartner cnpj -> 543 - ( cnpj, "cnpjs" ) 544 - 545 - PersonUid uid -> 546 - ( uid, "cnpjs" ) 576 + ConnectionQuery id1 id2 -> 577 + ( id1 ++ ";" ++ id2, "conexao" ) 547 578 548 579 549 580 queriesFor : String -> Bool -> Int -> List QueryRequest 550 - queriesFor id isCompany depth = 551 - if isCompany then 552 - [ QueryRequest id depth (CompanyCnpj id) 553 - , QueryRequest id depth (CompanyAsPartner id) 554 - ] 555 - 556 - else 557 - [ QueryRequest id depth (PersonUid id) ] 581 + queriesFor id _ depth = 582 + [ QueryRequest id depth (EntityQuery id) ] 558 583 559 584 560 585 enqueueQueries : List QueryRequest -> Model -> Model ··· 698 723 699 724 700 725 handleApiError : String -> String -> Maybe QueryRequest -> ApiError -> Model -> Model 701 - handleApiError id url currentQuery apiErr model = 702 - let 703 - wasPartnerLookup = 704 - case currentQuery |> Maybe.map .queryType of 705 - Just (CompanyAsPartner _) -> 706 - True 707 - 708 - _ -> 709 - False 710 - in 726 + handleApiError id url _ apiErr model = 711 727 case apiErr of 712 728 NotFound -> 713 - if wasPartnerLookup then 714 - model 715 - 716 - else 717 - markNodeError id (Just "Não encontrado") model 729 + markNodeError id (Just "Não encontrado") model 718 730 719 731 BadRequest msg -> 720 732 let
+21 -31
src/Types.elm
··· 9 9 import Url exposing (Url) 10 10 11 11 12 + type Tab 13 + = CnpjTab 14 + | ConnectionTab 15 + 16 + 12 17 type Entity 13 18 = Company String String -- Nome, CNPJ 14 19 | Person String (Maybe String) -- Nome, CPF ··· 33 38 34 39 35 40 type QueryType 36 - = CompanyCnpj String 37 - | CompanyAsPartner String 38 - | PersonUid String 41 + = EntityQuery String 42 + | ConnectionQuery String String 39 43 40 44 41 45 type alias QueryRequest = ··· 51 55 52 56 type alias Model = 53 57 { input : String 58 + , connectionInput1 : String 59 + , connectionInput2 : String 60 + , activeTab : Tab 54 61 , nodes : Dict String Node 55 62 , edges : Set ( String, String ) 56 63 , visited : Set QueryKey ··· 75 82 76 83 type Msg 77 84 = UpdateInput String 85 + | UpdateConnectionInput1 String 86 + | UpdateConnectionInput2 String 87 + | SwitchTab Tab 78 88 | Search 79 89 | NodeClicked String 80 90 | GotResponse String String Int (Result ApiError ApiResponse) ··· 90 100 | UrlChanged Url 91 101 92 102 93 - type ApiResponse 94 - = CompanyResponse CompanyData 95 - | PartnerResponse PartnerData 96 - 97 - 98 - type alias CompanyData = 99 - { id : String 100 - , name : String 101 - , partners : List PartnerRef 102 - } 103 - 104 - 105 - type alias PartnerRef = 106 - { id : String 107 - , name : Maybe String 108 - , cpf : Maybe String 109 - } 110 - 111 - 112 - type alias PartnerData = 113 - { id : String 114 - , name : Maybe String 115 - , cpf : Maybe String 116 - , companies : List CompanyRef 117 - } 103 + type alias ApiResponse = 104 + List Relation 118 105 119 106 120 - type alias CompanyRef = 121 - { id : String 122 - , name : String 107 + type alias Relation = 108 + { cnpj : String 109 + , razaoSocial : String 110 + , partnerId : String 111 + , partnerName : Maybe String 112 + , partnerCpf : Maybe String 123 113 }
+116 -22
src/View.elm
··· 50 50 [ nav [] 51 51 [ ul [] 52 52 [ li [] [ h1 [] [ text "Meu Garfo" ] ] 53 - ] 54 - , ul [ class "controls" ] 55 - [ li [] 56 - [ label [ for "cnpj-input" ] [ text "CNPJ" ] 57 - , input 58 - [ id "cnpj-input" 59 - , class "cnpj-input" 60 - , placeholder "00.000.000/0000-00" 61 - , value model.input 62 - , onInput UpdateInput 63 - , onEnter Search 64 - , attribute "aria-label" "CNPJ" 53 + , li [] 54 + [ a 55 + [ href "#" 56 + , preventDefaultOnClick (SwitchTab CnpjTab) 57 + , class 58 + (if model.activeTab == CnpjTab then 59 + "" 60 + 61 + else 62 + "secondary" 63 + ) 64 + , attribute "style" 65 + (if model.activeTab == CnpjTab then 66 + "font-weight: bold; border-bottom: 2px solid var(--pico-primary); border-radius: 0;" 67 + 68 + else 69 + "" 70 + ) 65 71 ] 66 - [] 72 + [ text "CNPJ" ] 67 73 ] 68 74 , li [] 69 - [ button 70 - [ onClick Search 71 - , Html.Attributes.disabled (model.currentQuery /= Nothing || not (List.isEmpty model.queryQueue)) 72 - , class "primary" 73 - ] 74 - [ text 75 - (if model.currentQuery /= Nothing || not (List.isEmpty model.queryQueue) then 76 - "Carregando..." 75 + [ a 76 + [ href "#" 77 + , preventDefaultOnClick (SwitchTab ConnectionTab) 78 + , class 79 + (if model.activeTab == ConnectionTab then 80 + "" 77 81 78 82 else 79 - "Buscar" 83 + "secondary" 84 + ) 85 + , attribute "style" 86 + (if model.activeTab == ConnectionTab then 87 + "font-weight: bold; border-bottom: 2px solid var(--pico-primary); border-radius: 0;" 88 + 89 + else 90 + "" 80 91 ) 81 92 ] 93 + [ text "Conexões" ] 82 94 ] 83 95 ] 96 + , ul [ class "controls" ] 97 + (case model.activeTab of 98 + CnpjTab -> 99 + [ li [] 100 + [ label [ for "cnpj-input" ] [ text "CNPJ" ] 101 + , input 102 + [ id "cnpj-input" 103 + , class "cnpj-input" 104 + , placeholder "00.000.000/0000-00" 105 + , value model.input 106 + , onInput UpdateInput 107 + , onEnter Search 108 + , attribute "aria-label" "CNPJ" 109 + ] 110 + [] 111 + ] 112 + , li [] 113 + [ button 114 + [ onClick Search 115 + , Html.Attributes.disabled (model.currentQuery /= Nothing || not (List.isEmpty model.queryQueue)) 116 + , class "primary" 117 + ] 118 + [ text 119 + (if model.currentQuery /= Nothing || not (List.isEmpty model.queryQueue) then 120 + "Carregando..." 121 + 122 + else 123 + "Buscar" 124 + ) 125 + ] 126 + ] 127 + ] 128 + 129 + ConnectionTab -> 130 + [ li [] 131 + [ label [ for "connection-input-1" ] [ text "Origem" ] 132 + , input 133 + [ id "connection-input-1" 134 + , class "cnpj-input" 135 + , placeholder "CNPJ ou ID" 136 + , value model.connectionInput1 137 + , onInput UpdateConnectionInput1 138 + , onEnter Search 139 + , attribute "aria-label" "Origem" 140 + ] 141 + [] 142 + ] 143 + , li [] 144 + [ label [ for "connection-input-2" ] [ text "Destino" ] 145 + , input 146 + [ id "connection-input-2" 147 + , class "cnpj-input" 148 + , placeholder "CNPJ ou ID" 149 + , value model.connectionInput2 150 + , onInput UpdateConnectionInput2 151 + , onEnter Search 152 + , attribute "aria-label" "Destino" 153 + ] 154 + [] 155 + ] 156 + , li [] 157 + [ button 158 + [ onClick Search 159 + , Html.Attributes.disabled (model.currentQuery /= Nothing || not (List.isEmpty model.queryQueue)) 160 + , class "primary" 161 + ] 162 + [ text 163 + (if model.currentQuery /= Nothing || not (List.isEmpty model.queryQueue) then 164 + "Carregando..." 165 + 166 + else 167 + "Buscar" 168 + ) 169 + ] 170 + ] 171 + ] 172 + ) 84 173 ] 85 174 ] 86 175 , main_ [ class "graph-viewport", onWheel Zoom ] ··· 127 216 onWheel : (Float -> Msg) -> Html.Attribute Msg 128 217 onWheel tagger = 129 218 preventDefaultOn "wheel" (Decode.map (\delta -> ( tagger delta, True )) (Decode.field "deltaY" Decode.float)) 219 + 220 + 221 + preventDefaultOnClick : msg -> Html.Attribute msg 222 + preventDefaultOnClick msg = 223 + preventDefaultOn "click" (Decode.succeed ( msg, True )) 130 224 131 225 132 226 onEnter : Msg -> Html.Attribute Msg