import './App.css' ;
import 'bootstrap/dist/css/bootstrap.min.css' ;
import { useState , useEffect } from 'react' ;
import Container from 'react-bootstrap/Container' ;
import Row from 'react-bootstrap/Row' ;
import Col from 'react-bootstrap/Col' ;
import Navbar from 'react-bootstrap/Navbar' ;
import Button from 'react-bootstrap/Button' ;
import ProgressBar from 'react-bootstrap/ProgressBar' ;
import Accordion from 'react-bootstrap/Accordion' ;
import ToastContainer from 'react-bootstrap/ToastContainer'
import Toast from 'react-bootstrap/Toast'
import Nav from 'react-bootstrap/Nav'
import Spinner from 'react-bootstrap/Spinner' ;
import { FaCheckCircle } from 'react-icons/fa' ;
import { IoCheckmarkCircleOutline , IoCloseCircleSharp , IoAlertCircleOutline } from 'react-icons/io5' ;
const parse = require ( 'csv-parse/lib/sync' )
const axios = require ( 'axios' ) ;
const dataSplitters = [ 0 , 128 , 225 , 363 ]
const BACKEND _URL = "https://projectdivar.com:4505"
const NOTIFICATIONTIMEOUT = 300 //In seconds
const progress1 = new Audio ( process . env . PUBLIC _URL + "/progress1.mp3" )
const progress2 = new Audio ( process . env . PUBLIC _URL + "/progress2.mp3" )
function ItemGroup ( p ) {
const { data } = p
const { contributor } = p
const [ displayData , setDisplayData ] = useState ( [ ] )
const [ lockout , setLockout ] = useState ( false )
useEffect ( ( ) => {
setDisplayData ( [ ... data ] . sort ( ( a , b ) => {
if ( b . required === b . obtained && a . required !== a . obtained ) { return - 1 }
if ( b . required === b . obtained && a . required === a . obtained ) { return a . id - b . id }
if ( b . required !== b . obtained && a . required !== a . obtained ) { return a . id - b . id }
return 0
} ) )
} , [ data ] )
useEffect ( ( ) => {
displayData . forEach ( ( item ) => {
if ( document . getElementById ( "field_" + item . id ) ) {
document . getElementById ( "field_" + item . id ) . value = item . obtained
}
} )
} , [ displayData ] )
function updateItem ( item , target , contributor ) {
var correctedVal = Math . min ( item . required , target . value ) ;
if ( correctedVal === Number ( item . obtained ) ) { return ; }
setLockout ( true )
axios . post ( BACKEND _URL + "/updateItem" , { obtained : correctedVal , id : item . id , last _modified : new Date ( ) , item _name : item . name , username : contributor , required : item . required , operation : correctedVal === Number ( item . required ) ? "FINISH" : correctedVal > item . obtained ? "INCREASE" : "SET" , previous _amt : item . obtained } )
. then ( ( data ) => {
setLockout ( false )
} )
. catch ( ( err ) => {
} )
}
return < Accordion . Item className = "bg-dark" eventKey = { p . akey } >
< Accordion . Header className = "panel-body bg-dark" > { p . name } < / A c c o r d i o n . H e a d e r >
< Accordion . Body className = "panel-body" >
{ displayData . map ( ( item , i , arr ) => < Row key = { item . id } className = { "pb-1 pt-1 text-light" + ( Number ( item . obtained ) === 0 ? " notStarted" : Number ( item . obtained ) === Number ( item . required ) ? " completed" : " inProgress" ) } >
< Col >
< img src = { "https://xivapi.com" + item . icon } alt = { item . name } / > { item . name }
< / C o l >
< Col >
< input disabled = { lockout } id = { "field_" + item . id } style = { { width : "5em" } } defaultValue = { item . obtained } className = "mt-1 bg-secondary"
onKeyDown = { ( k ) => {
if ( k . key === 'Enter' ) { updateItem ( item , document . getElementById ( "field_" + item . id ) , contributor ) }
} }
onChange = { ( f ) => {
if ( f . currentTarget . value >= item . required ) { f . currentTarget . blur ( ) }
} } onBlur = { ( f ) => { updateItem ( item , f . currentTarget , contributor ) } } type = "number" min = "0" max = { item . required } / > / { i t e m . r e q u i r e d } { i t e m . r e q u i r e d ! = = i t e m . o b t a i n e d & & < F a C h e c k C i r c l e s t y l e = { { c o l o r : " g r e e n " } } o n C l i c k = { ( f ) = > {
updateItem ( item , { value : item . required } , contributor )
} } / > }
< / C o l >
< Col >
< a style = { { position : "relative" , top : "8px" } } className = "text-muted" href = { "https://garlandtools.org/db/#item/" + item . itemid } target = "tools" > View Item Info < / a >
< / C o l >
< / R o w > ) }
< / A c c o r d i o n . B o d y >
< / A c c o r d i o n . I t e m >
}
function Notification ( p ) {
const [ show , setShow ] = useState ( true )
const { not } = p
return < Toast key = { not . id } show = { show } autohide delay = { NOTIFICATIONTIMEOUT * 1000 } onClose = { ( ) => { setShow ( false ) } } bg = { not . operation === "FINISH" ? "success" : not . operation === "INCREASE" ? "primary" : "warning" } >
< Toast . Header closeButton = { true } >
< span className = "me-auto" >
< strong > { not . username } < / s t r o n g >
{ not . operation === "FINISH" ? " has finished collecting " + not . required + "/" + not . required + " " + not . item _name + "!" :
not . operation === "INCREASE" ? " has collected " + not . obtained + "/" + not . required + " " + not . item _name + " (+" + ( not . obtained - not . previous _amt ) + ")"
: " has set " + not . item _name + " to " + not . obtained + "/" + not . required } < / s p a n >
< / T o a s t . H e a d e r >
< / T o a s t >
}
function DarkInput ( p ) {
return < input type = "text" className = "bg-dark text-white" { ... p } > < / i n p u t >
}
function DarkSelect ( p ) {
return < select className = "bg-dark text-white" { ... p } > { p . children } < / s e l e c t >
}
function ListApp ( p ) {
//https://xivapi.com/search?filters=ClassJob.ID=11,RecipeLevelTable.ClassJobLevel%3E=36,RecipeLevelTable.ClassJobLevel%3C=45&page=1
const { transferItems } = p
const [ groceryList , setGroceryList ] = useState ( [ ] )
const crafters = [ "CRP Carpenter" , "BSM Blacksmith" , "ARM Armorer" , "GSM Goldsmith" , "LTW Leatherworker" , "WVR Weaver" , "ALC Alchemist" , "CUL Culinarian" ]
const [ selectedCrafter , setSelectedCrafter ] = useState ( 0 )
const [ minLevel , setMinLevel ] = useState ( 1 )
const [ maxLevel , setMaxLevel ] = useState ( 10 )
const [ playerInventory , setPlayerInventory ] = useState ( [ ] )
const [ saddlebagInventory , setSaddlebagInventory ] = useState ( [ ] )
const [ r1Inventory , setR1Inventory ] = useState ( [ ] )
const [ r2Inventory , setR2Inventory ] = useState ( [ ] )
const [ r3Inventory , setR3Inventory ] = useState ( [ ] )
const [ r4Inventory , setR4Inventory ] = useState ( [ ] )
const [ r5Inventory , setR5Inventory ] = useState ( [ ] )
const [ r6Inventory , setR6Inventory ] = useState ( [ ] )
const [ r7Inventory , setR7Inventory ] = useState ( [ ] )
const [ r8Inventory , setR8Inventory ] = useState ( [ ] )
const [ r9Inventory , setR9Inventory ] = useState ( [ ] )
const [ checking , setChecking ] = useState ( false )
const [ lastUpdate , setLastUpdate ] = useState ( 0 )
const retainerNames = [ "Ayayayaya" , "Kittystorage" , "Morecatz" , "Finalretainer" , "Nowitsabun" , "Butwhy" , "Kkittyy" , "Howdoesanyonenameall" , "Ayayayayay" ] ;
function sleep ( ms ) {
return new Promise ( resolve => setTimeout ( resolve , ms ) ) ;
}
useEffect ( ( ) => {
const interval = setInterval ( ( ) => {
axios . get ( "http://projectdivar.com/inventoryData?lastUpdate=" + lastUpdate )
. then ( ( data ) => {
if ( data . data . length > 0 ) {
setLastUpdate ( new Date ( ) . getTime ( ) )
for ( var i = 0 ; i < data . data . length ; i ++ ) {
var inventory = data . data [ i ]
if ( i == 0 ) {
var plInven = [ ]
var sbInven = [ ]
for ( var j = 0 ; j < 140 ; j ++ ) {
plInven = [ ... plInven , inventory [ j ] ]
}
for ( var j = 140 ; j < inventory . length ; j ++ ) {
sbInven = [ ... sbInven , inventory [ j ] ]
}
setPlayerInventory ( plInven )
setSaddlebagInventory ( sbInven )
} else {
switch ( i ) {
case 1 : {
setR1Inventory ( inventory )
} break ;
case 2 : {
setR2Inventory ( inventory )
} break ;
case 3 : {
setR3Inventory ( inventory )
} break ;
case 4 : {
setR4Inventory ( inventory )
} break ;
case 5 : {
setR5Inventory ( inventory )
} break ;
case 6 : {
setR6Inventory ( inventory )
} break ;
case 7 : {
setR7Inventory ( inventory )
} break ;
case 8 : {
setR8Inventory ( inventory )
} break ;
case 9 : {
setR9Inventory ( inventory )
} break ;
}
}
}
}
} )
. catch ( ( err ) => { } )
} , 1000 )
return ( ) => clearInterval ( interval )
} )
function submitForm ( ) {
let classID = Number ( selectedCrafter ) + 8
let filter = "https://xivapi.com/search?filters=ClassJob.ID=" + classID + ",RecipeLevelTable.ClassJobLevel>=" + minLevel + ",RecipeLevelTable.ClassJobLevel<=" + maxLevel
let pageCount = 0
let currentPage = 1
let recipeData = [ ]
setChecking ( true )
axios . get ( encodeURI ( filter ) )
. then ( async ( resp ) => {
let data = resp . data
let pageCount = data . Pagination . PageTotal
let results = data . Results
while ( currentPage <= pageCount ) {
for ( let recipe of results ) {
axios . get ( encodeURI ( "https://xivapi.com" + recipe . Url ) )
. then ( ( resp ) => {
let d = resp . data
recipeData = [ d , ... recipeData ]
} )
await sleep ( 250 )
}
currentPage ++ ;
if ( currentPage > pageCount ) {
break ;
}
await axios . get ( encodeURI ( filter + "&page=" + currentPage ) )
. then ( ( resp ) => {
let d = resp . data
results = d . Results
} )
}
} )
. finally ( async ( ) => {
//console.log(recipeData)
//setChecking(false)
let craftedItems = { }
for ( var recipe of recipeData ) {
for ( let i = 0 ; i <= 5 ; i ++ ) {
var ingredient = recipe [ "ItemIngredient" + i ]
var amount = recipe [ "AmountIngredient" + i ]
if ( ingredient !== null ) {
if ( ingredient . Name . includes ( "Component Materials" ) || ingredient . Name . includes ( "Skybuilders'" ) ) { continue }
if ( ingredient . Name in craftedItems ) {
let oldItem = craftedItems [ ingredient . Name ] ;
oldItem . amt += Number ( amount )
craftedItems [ ingredient . Name ] = oldItem
} else {
craftedItems [ ingredient . Name ] = { ... ingredient , "amt" : Number ( amount ) }
}
}
}
}
let retainerItems = [ [ ] , [ ] , [ ] , [ ] , [ ] , [ ] , [ ] , [ ] , [ ] , [ ] ]
for ( var item of Object . keys ( craftedItems ) ) {
let found = false
let retainerArray = [ r1Inventory , r2Inventory , r3Inventory , r4Inventory , r5Inventory , r6Inventory , r7Inventory , r8Inventory , r9Inventory ]
for ( var i = 0 ; i < retainerArray . length ; i ++ ) {
for ( var j = 0 ; j < retainerArray [ i ] . length ; j ++ ) {
let it = retainerArray [ i ] [ j ]
if ( it . name === item ) {
var targetItem = { ... it }
targetItem . amt = craftedItems [ item ] . amt
targetItem . hq = false
targetItem . slot = j
retainerItems [ i ] = [ targetItem , ... retainerItems [ i ] ]
found = true
break ;
}
}
if ( found ) {
break ;
}
}
if ( found === false ) {
console . log ( JSON . stringify ( craftedItems [ item ] ) + " not found. Adding as " + JSON . stringify ( { "icon" : craftedItems [ item ] . Icon , "id" : craftedItems [ item ] . ID , "name" : craftedItems [ item ] . Name , "amt" : craftedItems [ item ] . amt , "hq" : false } ) )
retainerItems [ 9 ] = [ { "icon" : craftedItems [ item ] . Icon , "id" : craftedItems [ item ] . ID , "name" : craftedItems [ item ] . Name , "amt" : craftedItems [ item ] . amt , "hq" : false } , ... retainerItems [ 9 ] ]
}
}
for ( var i = 0 ; i < retainerItems . length - 1 ; i ++ ) {
retainerItems [ i ] . sort ( ( a , b ) => a . slot - b . slot )
}
setGroceryList ( retainerItems )
setChecking ( false )
} )
}
function Item ( p ) {
const { it } = p
return it . amt > 0 && < Row className = "text-white" >
< Col > < img src = { "https://xivapi.com" + it . icon } > < / i m g > { i t . s l o t ? " [ " + M a t h . f l o o r ( i t . s l o t / 3 5 + 1 ) + " ] " : " " } { i t . n a m e } x { i t . a m t } { i t . h q ? " ( H Q ) " : " " } < / C o l >
< / R o w >
}
function RetainerDisplay ( p ) {
const { id , inventory } = p
return < >
< Row className = "text-white" > < Col > < h2 > { retainerNames [ id ] } ' s Inventory < / h 2 > < / C o l > < / R o w >
{ inventory . map ( ( item , i ) => < Item it = { item } key = { i } > < / I t e m > ) }
< / >
}
return < >
< Row className = "text-white" >
< Col xs = { 4 } >
< DarkSelect value = { selectedCrafter } onChange = { ( f ) => { setSelectedCrafter ( f . currentTarget . value ) } } >
{ crafters . map ( ( crafter , i ) => < option value = { i } key = { i } > { crafter } < / o p t i o n > ) }
< / D a r k S e l e c t >
< / C o l >
< Col >
Level Range :
< / C o l >
< Col xs = { 2 } >
< DarkInput value = { minLevel } onChange = { ( f ) => { setMinLevel ( f . currentTarget . value ) } } > < / D a r k I n p u t >
< / C o l >
< Col > - < / C o l >
< Col xs = { 2 } >
< DarkInput value = { maxLevel } onChange = { ( f ) => { setMaxLevel ( f . currentTarget . value ) } } > < / D a r k I n p u t >
< / C o l >
< Col xs = { 3 } >
< Button onKeyDown = { ( k ) => { if ( k . key === 'Enter' ) { submitForm ( ) } } } disabled = { checking } onClick = { ( ) => { submitForm ( ) } } > { checking ? < Spinner animation = "border" > < / S p i n n e r > : " C o m p o s e " } < / B u t t o n >
< / C o l >
< / R o w >
< Row > < span > A < / s p a n > < / R o w >
{ groceryList . map ( ( list , i ) => {
if ( list . length > 0 ) {
return < >
< Row className = "text-white" > < Col > { ( i === 9 ) ? < h4 style = { { "color" : "#AA0000" } } > Not Found : < / h 4 > : < h 4 > F r o m { r e t a i n e r N a m e s [ i ] } : < / h 4 > } < / C o l > < / R o w >
< Row className = "text-white" > < Col >
< ul >
{ list . map ( ( item , j ) => < li > < Item it = { item } key = { j } > < / I t e m > < / l i > ) }
< / u l >
< / C o l > < / R o w >
< / >
}
} ) }
< Row > < span > Inventories < / s p a n > < / R o w >
< Row className = "text-white" > < Col > < h2 > Player Inventory < / h 2 > < / C o l > < / R o w >
{ playerInventory . map ( ( item , i ) => < Item it = { item } key = { i } > < / I t e m > ) }
< Row className = "text-white" > < Col > < h2 > Chocobo Saddlebag Inventory < / h 2 > < / C o l > < / R o w >
{ saddlebagInventory . map ( ( item , i ) => < Item it = { item } key = { i } > < / I t e m > ) }
< RetainerDisplay id = { 0 } inventory = { r1Inventory } > < / R e t a i n e r D i s p l a y >
< RetainerDisplay id = { 1 } inventory = { r2Inventory } > < / R e t a i n e r D i s p l a y >
< RetainerDisplay id = { 2 } inventory = { r3Inventory } > < / R e t a i n e r D i s p l a y >
< RetainerDisplay id = { 3 } inventory = { r4Inventory } > < / R e t a i n e r D i s p l a y >
< RetainerDisplay id = { 4 } inventory = { r5Inventory } > < / R e t a i n e r D i s p l a y >
< RetainerDisplay id = { 5 } inventory = { r6Inventory } > < / R e t a i n e r D i s p l a y >
< RetainerDisplay id = { 6 } inventory = { r7Inventory } > < / R e t a i n e r D i s p l a y >
< RetainerDisplay id = { 7 } inventory = { r8Inventory } > < / R e t a i n e r D i s p l a y >
< RetainerDisplay id = { 8 } inventory = { r9Inventory } > < / R e t a i n e r D i s p l a y >
< / >
}
function App ( ) {
const [ data , setData ] = useState ( [ ] )
const [ data2 , setData2 ] = useState ( [ ] )
const [ data3 , setData3 ] = useState ( [ ] )
const [ data4 , setData4 ] = useState ( [ ] )
const [ fileData , setFileData ] = useState ( )
const [ update , setUpdate ] = useState ( true )
const [ succeeded , setSucceeded ] = useState ( 0 )
const [ failed , setFailed ] = useState ( 0 )
const [ total , setTotal ] = useState ( 0 )
const [ lastModified , setLastModified ] = useState ( new Date ( ) )
const [ contributor , setContributor ] = useState ( "" )
const [ notifications , setNotifications ] = useState ( [ ] )
const [ nav , setNav ] = useState ( "main" )
const [ transferItems , setTransferItems ] = useState ( [ ] )
function LZ ( digits , numb ) {
return "0" . repeat ( digits - String ( numb ) . length ) + numb
}
useEffect ( ( ) => {
const interval = setInterval ( ( ) => {
axios . get ( BACKEND _URL + "/lastUpdate" )
. then ( ( data ) => {
if ( new Date ( data . data [ 0 ] . last _modified ) > lastModified ) {
console . log ( "Updating entries... " + [ lastModified , data . data [ 0 ] . last _modified ] )
setLastModified ( new Date ( ) )
return axios . get ( "https://projectdivar.com:4505/getData" )
. then ( ( data ) => {
//setData(data.data)
setData ( data . data . slice ( dataSplitters [ 0 ] , dataSplitters [ 1 ] ) )
setData2 ( data . data . slice ( dataSplitters [ 1 ] , dataSplitters [ 2 ] ) )
setData3 ( data . data . slice ( dataSplitters [ 2 ] , dataSplitters [ 3 ] ) )
setData4 ( data . data . slice ( dataSplitters [ 3 ] , data . data . length ) )
} )
}
} )
. catch ( ( err ) => {
console . log ( err . message )
} )
} , 1000 )
return ( ) => clearInterval ( interval )
} , [ lastModified ] )
useEffect ( ( ) => {
var notificationLastUpdate = new Date ( new Date ( ) - ( NOTIFICATIONTIMEOUT * 1000 ) )
var largestNotificationID = - 1
var dataCheck = true
const interval = setInterval ( ( ) => {
if ( dataCheck ) {
dataCheck = false
notificationLastUpdate = new Date ( new Date ( ) - ( NOTIFICATIONTIMEOUT * 1000 ) )
axios . get ( BACKEND _URL + "/getNotifications?date=" + encodeURIComponent ( LZ ( 4 , notificationLastUpdate . getUTCFullYear ( ) ) + "-" + LZ ( 2 , notificationLastUpdate . getUTCMonth ( ) + 1 ) + "-" + LZ ( 2 , notificationLastUpdate . getUTCDate ( ) ) + " " + LZ ( 2 , notificationLastUpdate . getUTCHours ( ) ) + ":" + LZ ( 2 , notificationLastUpdate . getUTCMinutes ( ) ) + ":" + LZ ( 2 , notificationLastUpdate . getUTCSeconds ( ) ) + "." + LZ ( 3 , notificationLastUpdate . getUTCMilliseconds ( ) ) + "+00" ) )
. then ( ( data ) => {
if ( data . data . length > 0 ) {
console . log ( "New notification array: " + JSON . stringify ( data . data ) )
setNotifications ( data . data )
var completion = false
var largestID = - 1
for ( var dat of data . data ) {
if ( dat . id > largestNotificationID && dat . operation === "FINISH" ) {
completion = true
largestID = Math . max ( dat . id , largestID )
break
} else
if ( dat . id > largestNotificationID )
{
largestID = Math . max ( dat . id , largestID )
}
}
if ( largestID !== - 1 ) {
largestNotificationID = largestID
if ( completion ) { progress2 . play ( ) } else { progress1 . play ( ) }
}
}
} )
. catch ( ( err ) => {
console . log ( err . message )
} )
. then ( ( ) => {
dataCheck = true
} )
}
} , 1000 )
return ( ) => clearInterval ( interval )
} , [ ] )
const disabled = true
useEffect ( ( ) => {
if ( update ) {
axios . get ( "https://projectdivar.com:4505/getData" )
. then ( ( data ) => {
//setData(data.data)
setData ( data . data . slice ( dataSplitters [ 0 ] , dataSplitters [ 1 ] ) )
setData2 ( data . data . slice ( dataSplitters [ 1 ] , dataSplitters [ 2 ] ) )
setData3 ( data . data . slice ( dataSplitters [ 2 ] , dataSplitters [ 3 ] ) )
setData4 ( data . data . slice ( dataSplitters [ 3 ] , data . data . length ) )
} )
. catch ( ( err ) => {
console . log ( err . message )
setData ( [
{ id : 1726 , itemid : 2 , name : "Fire Shard" , icon : "/i/020000/020001.png" , obtained : 694 , required : 4124 } ,
{ id : 1727 , itemid : 3 , name : "Ice Shard" , icon : "/i/020000/020003.png" , obtained : 0 , required : 1226 } ,
{ id : 1728 , itemid : 4 , name : "Wind Shard" , icon : "/i/020000/020004.png" , obtained : 4719 , required : 4719 } ,
{ id : 1729 , itemid : 5 , name : "Earth Shard" , icon : "/i/020000/020006.png" , obtained : 15 , required : 1764 } ,
{ id : 1730 , itemid : 6 , name : "Lightning Shard" , icon : "/i/020000/020005.png" , obtained : 0 , required : 2374 } ,
] )
} )
setUpdate ( false )
}
} , [ update ] )
useEffect ( ( ) => {
if ( succeeded + failed === total ) {
setUpdate ( true )
}
} , [ succeeded , failed , total ] )
useEffect ( ( ) => {
const downloadData = ( d , val , max ) => {
if ( val < max ) {
axios . get ( encodeURI ( "https://xivapi.com/search?string=" + d [ val ] . Item ) )
. then ( ( data ) => {
var results = data . data . Results
var found = false
for ( var r of results ) {
if ( r . Name === d [ val ] . Item && r . UrlType === "Item" ) {
found = true
//console.log("Found "+r)
setSucceeded ( succeeded + 1 )
var dataObj = {
itemid : r . ID ,
name : r . Name ,
obtained : 0 ,
required : d [ val ] . Needed ,
icon : r . Icon
}
return axios . post ( BACKEND _URL + "/setItem" , dataObj )
}
}
if ( ! found ) {
setFailed ( failed + 1 )
console . log ( "Could not find " + d [ val ] . Item + "...." )
}
} )
. then ( ( ) => {
setTimeout ( downloadData ( d , val + 1 , max ) , 250 )
} )
. catch ( ( err ) => {
setFailed ( failed + 1 )
} )
}
}
var d = parse ( fileData , { columns : true , skip _empty _lines : true } )
console . log ( d )
downloadData ( d , 0 , d . length )
setTotal ( d . length )
} , [ fileData , failed , succeeded ] )
return (
< Container className = "bg-dark" fluid >
< Navbar bg = "dark" variant = "dark" >
< Container >
< Navbar . Brand href = "#home" >
< img src = { process . env . PUBLIC _URL + "/favicon.ico" } width = "30" height = "30" className = "d-inline-block align-top" alt = "BUN logo" / > BUN Collab App
< / N a v b a r . B r a n d >
{ contributor . length > 0 && < >
< Nav . Link style = { ( nav === "main" ) ? { "color" : "lime" , "textDecoration" : "underline" } : { } } onClick = { ( ) => { setNav ( "main" ) } } > Main < / N a v . L i n k >
< Nav . Link style = { ( nav === "list" ) ? { "color" : "lime" , "textDecoration" : "underline" } : { } } onClick = { ( ) => { setNav ( "list" ) } } > Grocery List < / N a v . L i n k >
< span className = "text-light font-weight-light font-italic" > Signed in as { contributor } < / s p a n >
< / > }
< / C o n t a i n e r >
< / N a v b a r >
< Container >
{ contributor . length === 0 ? < >
< input placeHolder = "e.g. Bun" onKeyDown = { ( k ) => { if ( k . key === 'Enter' ) { setContributor ( document . getElementById ( "username" ) . value ) } } } id = "username" / >
< button type = "Submit" onClick = { ( f ) => { setContributor ( document . getElementById ( "username" ) . value ) } } > Submit < / b u t t o n >
< / > :
nav === "main" ?
data . length > 0 ?
< >
< Accordion className = "bg-dark" defaultActiveKey = "0" >
< ItemGroup name = "Gathering Items" contributor = { contributor } akey = "0" data = { data } lastModified = { lastModified } setLastModified = { setLastModified } setData = { setData } setData1 = { setData } setData2 = { setData2 } setData3 = { setData3 } setData4 = { setData4 } / >
< ItemGroup name = "Other Items" contributor = { contributor } akey = "1" data = { data2 } lastModified = { lastModified } setLastModified = { setLastModified } setData = { setData2 } setData1 = { setData } setData2 = { setData2 } setData3 = { setData3 } setData4 = { setData4 } / >
< ItemGroup name = "Pre-crafting" contributor = { contributor } akey = "2" data = { data3 } lastModified = { lastModified } setLastModified = { setLastModified } setData = { setData3 } setData1 = { setData } setData2 = { setData2 } setData3 = { setData3 } setData4 = { setData4 } / >
< ItemGroup name = "Crafting Items" contributor = { contributor } akey = "3" data = { data4 } lastModified = { lastModified } setLastModified = { setLastModified } setData = { setData4 } setData1 = { setData } setData2 = { setData2 } setData3 = { setData3 } setData4 = { setData4 } / >
< / A c c o r d i o n >
< / > :
! disabled &&
< Row >
< Col >
{ total === 0 ? < caption > < label className = "buttonLabel" for = "uploads" > Import List < / l a b e l > < i n p u t o n C h a n g e = { ( f ) = > {
const reader = new FileReader ( )
reader . onload = ( ev ) => {
setFileData ( ev . target . result )
}
reader . readAsText ( f . target . files [ 0 ] )
} } style = { { opacity : 0 } } id = "uploads" type = "file" accept = ".txt,.csv" / > < / c a p t i o n > :
< >
< ProgressBar >
< ProgressBar animated striped variant = "success" now = { Math . round ( succeeded / total ) * 100 } / >
< ProgressBar animated striped variant = "danger" now = { Math . round ( failed / total ) * 100 } / >
< / P r o g r e s s B a r >
< div className = "text-center" > { Math . round ( ( ( failed + succeeded ) / total ) * 100 ) + "%" } < / d i v >
< / >
}
< / C o l >
< / R o w >
:
nav === "list" ? < ListApp transferItems = { transferItems } > < / L i s t A p p > :
< > < / >
}
< div style = { { pointerEvents : "none" , position : "fixed" , top : "0px" , left : "0px" , width : "100%" , height : "100%" } } >
< ToastContainer position = "bottom-end" >
{ notifications . map ( ( not ) => {
return < Notification key = { not . id } not = { not } / >
} ) }
< / T o a s t C o n t a i n e r >
< / d i v >
< / C o n t a i n e r >
< / C o n t a i n e r >
) ;
}
export default App ;