2020-08-30 12:18:16 +02:00
Grocy . Components . ProductCard = { } ;
2018-04-14 11:10:38 +02:00
Grocy . Components . ProductCard . Refresh = function ( productId )
{
2019-01-19 14:51:51 +01:00
Grocy . Api . Get ( 'stock/products/' + productId ,
2018-04-14 11:10:38 +02:00
function ( productDetails )
{
2018-07-27 19:39:34 +02:00
var stockAmount = productDetails . stock _amount || '0' ;
2020-08-17 14:47:33 -05:00
var stockValue = productDetails . stock _value || '0' ;
2018-11-18 13:17:36 +01:00
var stockAmountOpened = productDetails . stock _amount _opened || '0' ;
2018-04-14 11:10:38 +02:00
$ ( '#productcard-product-name' ) . text ( productDetails . product . name ) ;
2019-08-15 14:35:28 +02:00
$ ( '#productcard-product-description' ) . html ( productDetails . product . description ) ;
2018-07-27 19:39:34 +02:00
$ ( '#productcard-product-stock-amount' ) . text ( stockAmount ) ;
2022-02-06 21:09:34 +01:00
$ ( '#productcard-product-stock-qu-name' ) . text ( _ _n ( stockAmount , productDetails . quantity _unit _stock . name , productDetails . quantity _unit _stock . name _plural , true ) ) ;
2022-04-02 17:49:35 +02:00
$ ( '#productcard-product-stock-value' ) . text ( stockValue ) ;
2019-09-21 13:40:31 +02:00
$ ( '#productcard-product-last-purchased' ) . text ( ( productDetails . last _purchased || '2999-12-31' ) . substring ( 0 , 10 ) ) ;
$ ( '#productcard-product-last-purchased-timeago' ) . attr ( "datetime" , productDetails . last _purchased || '2999-12-31' ) ;
$ ( '#productcard-product-last-used' ) . text ( ( productDetails . last _used || '2999-12-31' ) . substring ( 0 , 10 ) ) ;
$ ( '#productcard-product-last-used-timeago' ) . attr ( "datetime" , productDetails . last _used || '2999-12-31' ) ;
2020-01-17 11:06:33 -06:00
if ( productDetails . location != null )
{
$ ( '#productcard-product-location' ) . text ( productDetails . location . name ) ;
}
2023-02-06 20:22:10 +01:00
$ ( '#productcard-product-spoil-rate' ) . text ( ( productDetails . spoil _rate _percent / 100 ) . toLocaleString ( undefined , { style : "percent" } ) ) ;
2019-04-22 10:11:58 +02:00
2019-09-17 16:18:00 +02:00
if ( productDetails . is _aggregated _amount == 1 )
{
$ ( '#productcard-product-stock-amount-aggregated' ) . text ( productDetails . stock _amount _aggregated ) ;
2022-02-06 21:09:34 +01:00
$ ( '#productcard-product-stock-qu-name-aggregated' ) . text ( _ _n ( productDetails . stock _amount _aggregated , productDetails . quantity _unit _stock . name , productDetails . quantity _unit _stock . name _plural , true ) ) ;
2019-09-17 16:18:00 +02:00
if ( productDetails . stock _amount _opened _aggregated > 0 )
{
2023-05-22 21:53:15 +02:00
$ ( '#productcard-product-stock-opened-amount-aggregated' ) . text ( _ _t ( '%s opened' , productDetails . stock _amount _opened _aggregated . toLocaleString ( { minimumFractionDigits : 0 , maximumFractionDigits : Grocy . UserSettings . stock _decimal _places _amounts } ) ) ) ;
2019-09-17 16:18:00 +02:00
}
else
{
$ ( '#productcard-product-stock-opened-amount-aggregated' ) . text ( "" ) ;
}
$ ( "#productcard-aggregated-amounts" ) . removeClass ( "d-none" ) ;
}
else
{
$ ( "#productcard-aggregated-amounts" ) . addClass ( "d-none" ) ;
}
2023-02-06 20:22:10 +01:00
if ( productDetails . product . description )
2019-04-22 10:11:58 +02:00
{
$ ( "#productcard-product-description-wrapper" ) . removeClass ( "d-none" ) ;
}
else
{
$ ( "#productcard-product-description-wrapper" ) . addClass ( "d-none" ) ;
}
if ( productDetails . average _shelf _life _days == - 1 )
{
2019-05-01 20:19:18 +02:00
$ ( '#productcard-product-average-shelf-life' ) . text ( _ _t ( "Unknown" ) ) ;
2019-04-22 10:11:58 +02:00
}
2023-02-06 20:22:10 +01:00
else if ( productDetails . average _shelf _life _days > 73000 ) // > 200 years aka forever
2019-09-27 14:04:44 +02:00
{
$ ( '#productcard-product-average-shelf-life' ) . text ( _ _t ( "Unlimited" ) ) ;
}
2019-04-22 10:11:58 +02:00
else
{
$ ( '#productcard-product-average-shelf-life' ) . text ( moment . duration ( productDetails . average _shelf _life _days , "days" ) . humanize ( ) ) ;
}
2018-04-14 11:10:38 +02:00
2018-11-18 13:17:36 +01:00
if ( stockAmountOpened > 0 )
{
2023-05-22 21:53:15 +02:00
$ ( '#productcard-product-stock-opened-amount' ) . text ( _ _t ( '%s opened' , stockAmountOpened . toLocaleString ( { minimumFractionDigits : 0 , maximumFractionDigits : Grocy . UserSettings . stock _decimal _places _amounts } ) ) ) ;
2018-11-18 13:17:36 +01:00
}
else
{
$ ( '#productcard-product-stock-opened-amount' ) . text ( "" ) ;
}
2019-09-22 09:03:59 +02:00
$ ( '#productcard-product-edit-button' ) . attr ( "href" , U ( "/product/" + productDetails . product . id . toString ( ) + '?' + 'returnto=' + encodeURIComponent ( Grocy . CurrentUrlRelative ) ) ) ;
2019-09-27 16:54:40 +02:00
$ ( '#productcard-product-journal-button' ) . attr ( "href" , U ( "/stockjournal?embedded&product=" + productDetails . product . id . toString ( ) ) ) ;
2025-01-16 21:34:01 +01:00
$ ( '#productcard-product-shoppinglist-button' ) . attr ( "href" , U ( "/shoppinglistitem/new?embedded&updateexistingproduct&list=1&product=" + productDetails . product . id . toString ( ) ) ) ;
2020-11-08 15:09:10 +01:00
$ ( '#productcard-product-stock-button' ) . attr ( "href" , U ( "/stockentries?embedded&product=" + productDetails . product . id . toString ( ) ) ) ;
2020-10-20 13:14:25 -05:00
$ ( '#productcard-product-stock-button' ) . removeClass ( "disabled" ) ;
2018-10-22 19:13:08 +02:00
$ ( '#productcard-product-edit-button' ) . removeClass ( "disabled" ) ;
2019-09-27 14:19:06 +02:00
$ ( '#productcard-product-journal-button' ) . removeClass ( "disabled" ) ;
2025-01-16 21:34:01 +01:00
$ ( '#productcard-product-shoppinglist-button' ) . removeClass ( "disabled" ) ;
2018-10-22 19:13:08 +02:00
2018-07-26 20:27:38 +02:00
if ( productDetails . last _price !== null )
{
2023-05-18 13:37:13 +02:00
$ ( '#productcard-product-last-price' ) . text ( _ _t ( "%1$s per %2$s" , ( productDetails . last _price * productDetails . qu _conversion _factor _price _to _stock ) . toLocaleString ( undefined , { style : "currency" , currency : Grocy . Currency , minimumFractionDigits : Grocy . UserSettings . stock _decimal _places _prices _display , maximumFractionDigits : Grocy . UserSettings . stock _decimal _places _prices _display } ) , productDetails . quantity _unit _price . name ) ) ;
2023-02-06 20:22:10 +01:00
$ ( '#productcard-product-last-price' ) . attr ( "data-original-title" , _ _t ( "%1$s per %2$s" , productDetails . last _price . toLocaleString ( undefined , { style : "currency" , currency : Grocy . Currency , minimumFractionDigits : Grocy . UserSettings . stock _decimal _places _prices _display , maximumFractionDigits : Grocy . UserSettings . stock _decimal _places _prices _display } ) , productDetails . quantity _unit _stock . name ) ) ;
2018-07-26 20:27:38 +02:00
}
else
{
2019-05-01 20:19:18 +02:00
$ ( '#productcard-product-last-price' ) . text ( _ _t ( 'Unknown' ) ) ;
2022-04-02 17:49:35 +02:00
$ ( '#productcard-product-last-price' ) . removeAttr ( "data-original-title" ) ;
2018-07-26 20:27:38 +02:00
}
2020-08-17 14:47:33 -05:00
if ( productDetails . avg _price !== null )
{
2023-05-18 13:37:13 +02:00
$ ( '#productcard-product-average-price' ) . text ( _ _t ( "%1$s per %2$s" , ( productDetails . avg _price * productDetails . qu _conversion _factor _price _to _stock ) . toLocaleString ( undefined , { style : "currency" , currency : Grocy . Currency , minimumFractionDigits : Grocy . UserSettings . stock _decimal _places _prices _display , maximumFractionDigits : Grocy . UserSettings . stock _decimal _places _prices _display } ) , productDetails . quantity _unit _price . name ) ) ;
2023-02-06 20:22:10 +01:00
$ ( '#productcard-product-average-price' ) . attr ( "data-original-title" , _ _t ( "%1$s per %2$s" , productDetails . avg _price . toLocaleString ( undefined , { style : "currency" , currency : Grocy . Currency , minimumFractionDigits : Grocy . UserSettings . stock _decimal _places _prices _display , maximumFractionDigits : Grocy . UserSettings . stock _decimal _places _prices _display } ) , productDetails . quantity _unit _stock . name ) ) ;
2020-08-17 14:47:33 -05:00
}
else
{
$ ( '#productcard-product-average-price' ) . text ( _ _t ( 'Unknown' ) ) ;
2022-04-02 17:49:35 +02:00
$ ( ) . removeAttr ( "data-original-title" ) ;
2020-08-17 14:47:33 -05:00
}
2023-02-06 20:22:10 +01:00
if ( productDetails . product . picture _file _name )
2018-10-01 20:20:50 +02:00
{
$ ( "#productcard-product-picture" ) . removeClass ( "d-none" ) ;
2019-09-18 13:59:37 +02:00
$ ( "#productcard-product-picture" ) . attr ( "src" , U ( '/api/files/productpictures/' + btoa ( productDetails . product . picture _file _name ) + '?force_serve_as=picture&best_fit_width=400' ) ) ;
2018-10-01 20:20:50 +02:00
}
else
{
$ ( "#productcard-product-picture" ) . addClass ( "d-none" ) ;
}
2022-04-01 18:49:17 +02:00
$ ( "#productcard-product-stock-amount-wrapper" ) . removeClass ( "d-none" ) ;
$ ( "#productcard-aggregated-amounts" ) . addClass ( "pl-2" ) ;
if ( productDetails . product . no _own _stock == 1 )
{
$ ( "#productcard-product-stock-amount-wrapper" ) . addClass ( "d-none" ) ;
$ ( "#productcard-aggregated-amounts" ) . removeClass ( "pl-2" ) ;
}
2020-01-27 19:00:49 +01:00
RefreshContextualTimeago ( ".productcard" ) ;
2022-04-01 18:49:17 +02:00
RefreshLocaleNumberDisplay ( ".productcard" ) ;
2018-07-26 20:27:38 +02:00
2022-04-02 17:49:35 +02:00
if ( Grocy . FeatureFlags . GROCY _FEATURE _FLAG _STOCK _PRICE _TRACKING )
2018-07-26 20:27:38 +02:00
{
2022-04-02 17:49:35 +02:00
Grocy . Api . Get ( 'stock/products/' + productId + '/price-history' ,
function ( priceHistoryDataPoints )
2019-09-19 17:46:52 +02:00
{
2022-04-02 17:49:35 +02:00
if ( priceHistoryDataPoints . length > 0 )
2020-03-25 19:49:10 +01:00
{
2022-04-02 17:49:35 +02:00
$ ( "#productcard-product-price-history-chart" ) . removeClass ( "d-none" ) ;
$ ( "#productcard-no-price-data-hint" ) . addClass ( "d-none" ) ;
2020-08-29 16:41:27 +02:00
2022-04-02 17:49:35 +02:00
Grocy . Components . ProductCard . ReInitPriceHistoryChart ( ) ;
2025-01-14 17:54:06 +01:00
2022-04-02 17:49:35 +02:00
var datasets = { } ;
2025-01-14 17:54:06 +01:00
datasets [ "_TrendlineDataset" ] = [ ]
2022-04-02 17:49:35 +02:00
var chart = Grocy . Components . ProductCard . PriceHistoryChart . data ;
priceHistoryDataPoints . forEach ( ( dataPoint ) =>
{
var key = _ _t ( "Unknown store" ) ;
if ( dataPoint . shopping _location )
{
key = dataPoint . shopping _location . name
}
if ( ! datasets [ key ] )
{
datasets [ key ] = [ ]
}
2025-01-14 17:54:06 +01:00
2022-04-02 17:49:35 +02:00
chart . labels . push ( moment ( dataPoint . date ) . toDate ( ) ) ;
2023-05-18 13:37:13 +02:00
datasets [ key ] . push ( { x : moment ( dataPoint . date ) . toDate ( ) , y : dataPoint . price * productDetails . qu _conversion _factor _price _to _stock } ) ;
2025-01-14 17:54:06 +01:00
datasets [ "_TrendlineDataset" ] . push ( { x : moment ( dataPoint . date ) . toDate ( ) , y : dataPoint . price * productDetails . qu _conversion _factor _price _to _stock } ) ;
2022-04-02 17:49:35 +02:00
} ) ;
2025-01-14 17:54:06 +01:00
2022-04-02 17:49:35 +02:00
Object . keys ( datasets ) . forEach ( ( key ) =>
{
2025-01-14 17:54:06 +01:00
if ( key != "_TrendlineDataset" )
{
2025-08-15 16:55:50 +02:00
var color = "HSL(" + ( 129 * chart . datasets . length ) + ",100%,50%)" ;
2025-01-14 17:54:06 +01:00
chart . datasets . push ( {
data : datasets [ key ] ,
2025-08-15 16:55:50 +02:00
label : key ,
2025-01-14 17:54:06 +01:00
fill : false ,
2025-08-15 16:55:50 +02:00
borderColor : color ,
pointBackgroundColor : color ,
pointBorderColor : color ,
pointHoverBackgroundColor : color
2025-01-14 17:54:06 +01:00
} ) ;
}
else
{
chart . datasets . push ( {
data : datasets [ key ] ,
fill : false ,
borderColor : "HSL(" + ( 129 * chart . datasets . length ) + ",100%,50%)" ,
label : key ,
hidden : true ,
alwaysShowTrendline : true ,
trendlineLinear : {
colorMin : "rgba(0, 0, 0, 0.3)" ,
colorMax : "rgba(0, 0, 0, 0.3)" ,
lineStyle : "dotted" ,
width : 3
}
} ) ;
}
2022-04-02 17:49:35 +02:00
} ) ;
2022-06-09 22:14:00 +02:00
2022-04-02 17:49:35 +02:00
Grocy . Components . ProductCard . PriceHistoryChart . update ( ) ;
}
else
2020-08-30 12:18:16 +02:00
{
2022-04-02 17:49:35 +02:00
$ ( "#productcard-product-price-history-chart" ) . addClass ( "d-none" ) ;
$ ( "#productcard-no-price-data-hint" ) . removeClass ( "d-none" ) ;
2020-03-25 19:34:56 +01:00
}
2022-04-02 17:49:35 +02:00
} ,
function ( xhr )
2020-08-30 12:18:16 +02:00
{
2022-04-02 17:49:35 +02:00
console . error ( xhr ) ;
}
) ;
2018-07-26 20:27:38 +02:00
}
2022-04-02 17:49:35 +02:00
} ,
function ( xhr )
{
console . error ( xhr ) ;
}
) ;
2018-04-14 11:10:38 +02:00
} ;
2018-07-26 20:27:38 +02:00
Grocy . Components . ProductCard . ReInitPriceHistoryChart = function ( )
{
if ( typeof Grocy . Components . ProductCard . PriceHistoryChart !== "undefined" )
{
Grocy . Components . ProductCard . PriceHistoryChart . destroy ( ) ;
}
var format = 'YYYY-MM-DD' ;
Grocy . Components . ProductCard . PriceHistoryChart = new Chart ( document . getElementById ( "productcard-product-price-history-chart" ) , {
type : "line" ,
data : {
labels : [ //Date objects
// Will be populated in Grocy.Components.ProductCard.Refresh
] ,
2020-03-25 19:34:56 +01:00
datasets : [ //Datasets
// Will be populated in Grocy.Components.ProductCard.Refresh
]
2018-07-26 20:27:38 +02:00
} ,
options : {
scales : {
xAxes : [ {
type : 'time' ,
time : {
parser : format ,
round : 'day' ,
tooltipFormat : format ,
unit : 'day' ,
unitStepSize : 10 ,
displayFormats : {
'day' : format
}
} ,
ticks : {
autoSkip : true ,
maxRotation : 0
}
} ] ,
yAxes : [ {
ticks : {
2022-04-02 17:49:35 +02:00
beginAtZero : true ,
callback : function ( value , index , ticks )
{
2022-06-04 14:09:35 +02:00
return Number . parseFloat ( value ) . toLocaleString ( undefined , { style : "currency" , currency : Grocy . Currency , minimumFractionDigits : Grocy . UserSettings . stock _decimal _places _prices _display , maximumFractionDigits : Grocy . UserSettings . stock _decimal _places _prices _display } ) ;
2022-04-02 17:49:35 +02:00
}
2018-07-26 20:27:38 +02:00
}
} ]
} ,
legend : {
2025-01-14 17:54:06 +01:00
display : true ,
labels : {
filter : function ( item , chart )
{
return item . text != "_TrendlineDataset" ;
}
}
2022-04-02 17:49:35 +02:00
} ,
tooltips : {
callbacks : {
label : function ( tooltipItem , data )
{
var label = data . datasets [ tooltipItem . datasetIndex ] . label || '' ;
if ( label )
{
label += ': ' ;
}
2022-06-04 14:09:35 +02:00
label += tooltipItem . yLabel . toLocaleString ( undefined , { style : "currency" , currency : Grocy . Currency , minimumFractionDigits : Grocy . UserSettings . stock _decimal _places _prices _display , maximumFractionDigits : Grocy . UserSettings . stock _decimal _places _prices _display } )
2022-04-02 17:49:35 +02:00
return label ;
}
}
2018-07-26 20:27:38 +02:00
}
}
} ) ;
}
2019-04-22 10:11:58 +02:00
$ ( "#productcard-product-description" ) . on ( "shown.bs.collapse" , function ( )
{
2019-05-01 20:19:18 +02:00
$ ( ".expandable-text" ) . find ( "a[data-toggle='collapse']" ) . text ( _ _t ( "Show less" ) ) ;
2019-04-22 10:11:58 +02:00
} )
$ ( "#productcard-product-description" ) . on ( "hidden.bs.collapse" , function ( )
{
2019-05-01 20:19:18 +02:00
$ ( ".expandable-text" ) . find ( "a[data-toggle='collapse']" ) . text ( _ _t ( "Show more" ) ) ;
2019-04-22 10:11:58 +02:00
} )
2023-05-23 17:32:54 +02:00
$ ( document ) . on ( "click" , ".productcard-trigger" , function ( e )
{
var productId = $ ( e . currentTarget ) . attr ( "data-product-id" ) ;
if ( productId != "" )
{
Grocy . Components . ProductCard . Refresh ( productId ) ;
$ ( "#productcard-modal" ) . modal ( "show" ) ;
}
} ) ;