IDEMPIERE-3562 Timeline view of record changes.

This commit is contained in:
Heng Sin Low 2019-03-28 16:45:41 +08:00
parent 6265a1bc5c
commit e65edd9e51
12 changed files with 506 additions and 21 deletions

View File

@ -0,0 +1,11 @@
SET SQLBLANKLINES ON
SET DEFINE OFF
-- IDEMPIERE-3562 Timeline view of record changes
-- Mar 28, 2019, 3:57:53 PM MYT
INSERT INTO AD_Message (MsgType,MsgText,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,AD_Message_ID,Value,EntityType,AD_Message_UU) VALUES ('I','Time Line',0,0,'Y',TO_DATE('2019-03-28 15:57:52','YYYY-MM-DD HH24:MI:SS'),100,TO_DATE('2019-03-28 15:57:52','YYYY-MM-DD HH24:MI:SS'),100,200491,'TimeLine','D','744a1b33-8052-457e-b763-73a75d7b4d52')
;
SELECT register_migration_script('201903281500_IDEMPIERE-3562.sql') FROM dual
;

View File

@ -0,0 +1,8 @@
-- IDEMPIERE-3562 Timeline view of record changes
-- Mar 28, 2019, 3:57:53 PM MYT
INSERT INTO AD_Message (MsgType,MsgText,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,AD_Message_ID,Value,EntityType,AD_Message_UU) VALUES ('I','Time Line',0,0,'Y',TO_TIMESTAMP('2019-03-28 15:57:52','YYYY-MM-DD HH24:MI:SS'),100,TO_TIMESTAMP('2019-03-28 15:57:52','YYYY-MM-DD HH24:MI:SS'),100,200491,'TimeLine','D','744a1b33-8052-457e-b763-73a75d7b4d52')
;
SELECT register_migration_script('201903281500_IDEMPIERE-3562.sql') FROM dual
;

View File

@ -131,6 +131,7 @@ public class SystemIDs
public final static int REFERENCE_AD_USER = 110;
public final static int REFERENCE_DOCUMENTACTION = 135;
public final static int REFERENCE_DOCUMENTSTATUS = 131;
public final static int REFERENCE_PAYMENTRULE = 195;
public final static int REFERENCE_POSTING_TYPE = 125;
public final static int REFERENCE_POSTED = 234;

View File

@ -51,6 +51,7 @@ import org.compiere.model.MRMA;
import org.compiere.model.MRole;
import org.compiere.model.MTable;
import org.compiere.model.PO;
import org.compiere.model.SystemIDs;
import org.compiere.util.CLogger;
import org.compiere.util.DB;
import org.compiere.util.Env;
@ -1354,4 +1355,62 @@ public class DocumentEngine implements DocAction
return success;
}
/**
* Fill Vector with DocAction Ref_List(131) values
* @param v_value
* @param v_name
* @param v_description
*/
public static void readStatusReferenceList(ArrayList<String> v_value, ArrayList<String> v_name,
ArrayList<String> v_description)
{
if (v_value == null)
throw new IllegalArgumentException("v_value parameter is null");
if (v_name == null)
throw new IllegalArgumentException("v_name parameter is null");
if (v_description == null)
throw new IllegalArgumentException("v_description parameter is null");
String sql;
if (Env.isBaseLanguage(Env.getCtx(), "AD_Ref_List"))
sql = "SELECT Value, Name, Description FROM AD_Ref_List "
+ "WHERE AD_Reference_ID=? ORDER BY Name";
else
sql = "SELECT l.Value, t.Name, t.Description "
+ "FROM AD_Ref_List l, AD_Ref_List_Trl t "
+ "WHERE l.AD_Ref_List_ID=t.AD_Ref_List_ID"
+ " AND t.AD_Language='" + Env.getAD_Language(Env.getCtx()) + "'"
+ " AND l.AD_Reference_ID=? ORDER BY t.Name";
PreparedStatement pstmt = null;
ResultSet rs = null;
try
{
pstmt = DB.prepareStatement(sql, null);
pstmt.setInt(1, SystemIDs.REFERENCE_DOCUMENTSTATUS);
rs = pstmt.executeQuery();
while (rs.next())
{
String value = rs.getString(1);
String name = rs.getString(2);
String description = rs.getString(3);
if (description == null)
description = "";
//
v_value.add(value);
v_name.add(name);
v_description.add(description);
}
}
catch (SQLException e)
{
log.log(Level.SEVERE, sql, e);
}
finally
{
DB.close(rs, pstmt);
rs = null;
pstmt = null;
}
}
} // DocumentEnine

View File

@ -1390,7 +1390,7 @@ public abstract class AbstractADWindowContent extends AbstractUIPart implements
if (logger.isLoggable(Level.INFO)) logger.info(dbInfo);
if (adTabbox.getSelectedGridTab() != null && adTabbox.getSelectedGridTab().isQueryActive())
dbInfo = "[ " + dbInfo + " ]";
breadCrumb.setStatusDB(dbInfo, e);
breadCrumb.setStatusDB(dbInfo, e, adTabbox.getSelectedGridTab());
String adInfo = e.getAD_Message();
if ( adInfo == null

View File

@ -32,6 +32,7 @@ import org.adempiere.webui.theme.ThemeManager;
import org.adempiere.webui.util.ZKUpdateUtil;
import org.adempiere.webui.window.WRecordInfo;
import org.compiere.model.DataStatusEvent;
import org.compiere.model.GridTab;
import org.compiere.model.MRole;
import org.compiere.util.Env;
import org.compiere.util.Msg;
@ -88,6 +89,8 @@ public class BreadCrumb extends Div implements EventListener<Event> {
protected Menupopup linkPopup;
private GridTab m_gridTab;
/**
*
*/
@ -300,7 +303,7 @@ public class BreadCrumb extends Div implements EventListener<Event> {
return;
String title = Msg.getMsg(Env.getCtx(), "Who") + m_text;
new WRecordInfo (title, m_dse);
new WRecordInfo (title, m_dse, m_gridTab);
} else if (event.getTarget() == btnFirst) {
if (toolbarListener != null)
toolbarListener.onFirst();
@ -409,14 +412,15 @@ public class BreadCrumb extends Div implements EventListener<Event> {
*/
public void setStatusDB (String text)
{
setStatusDB(text, null);
setStatusDB(text, null, null);
}
/**
* @param text
* @param dse
* @param gridTab
*/
public void setStatusDB (String text, DataStatusEvent dse)
public void setStatusDB (String text, DataStatusEvent dse, GridTab gridTab)
{
if (text == null || text.length() == 0)
{
@ -435,6 +439,7 @@ public class BreadCrumb extends Div implements EventListener<Event> {
enableFirstNavigation(m_dse.getCurrentRow() > 0);
enableLastNavigation(m_dse.getTotalRows() > m_dse.getCurrentRow()+1);
}
m_gridTab = gridTab;
}
@Override

View File

@ -322,7 +322,7 @@ public class StatusBarPanel extends Panel implements EventListener<Event>, IStat
return;
String title = Msg.getMsg(Env.getCtx(), "Who") + m_text;
new WRecordInfo (title, m_dse);
new WRecordInfo (title, m_dse, null);
}
else if (Events.ON_CLICK.equals(event.getName()) && event.getTarget() == popup)
{

View File

@ -0,0 +1,283 @@
/***********************************************************************
* This file is part of iDempiere ERP Open Source *
* http://www.idempiere.org *
* *
* Copyright (C) Contributors *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License *
* as published by the Free Software Foundation; either version 2 *
* of the License, or (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the Free Software *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, *
* MA 02110-1301, USA. *
* *
* Contributors: *
* - hengsin *
**********************************************************************/
package org.adempiere.webui.window;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
import org.compiere.model.GridField;
import org.compiere.model.GridTab;
import org.compiere.model.MUser;
import org.compiere.process.DocAction;
import org.compiere.process.DocumentEngine;
import org.compiere.util.DB;
import org.compiere.util.DisplayType;
import org.compiere.util.Env;
import org.compiere.util.Msg;
import org.zkoss.util.Pair;
import org.zkoss.zul.Hbox;
import org.zkoss.zul.Html;
import org.zkoss.zul.Vlayout;
/**
* @author hengsin
*
*/
public class RecordTimeLinePanel extends Vlayout {
/**
* generated serial id
*/
private static final long serialVersionUID = 3420422470180313180L;
/**
*
*/
public RecordTimeLinePanel() {
setStyle("height:100%;width:100%;overflow:auto");
}
public void render(GridTab gridTab) {
getChildren().clear();
if (gridTab == null) {
return;
}
if (gridTab.getRowCount() == 0 || gridTab.isNew()) {
return;
}
int recordId = gridTab.getRecord_ID();
int tableId = gridTab.getAD_Table_ID();
ArrayList<String> docActionValues = new ArrayList<String>();
ArrayList<String> docActionNames = new ArrayList<String>();
DocumentEngine.readReferenceList(docActionValues, docActionNames, new ArrayList<String>());
ArrayList<String> docStatusValues = new ArrayList<String>();
ArrayList<String> docStatusNames = new ArrayList<String>();
DocumentEngine.readStatusReferenceList(docStatusValues, docStatusNames, new ArrayList<String>());
String reversedStatusName = null;
for(int i = 0; i < docStatusValues.size(); i++) {
if (DocAction.STATUS_Reversed.equals(docStatusValues.get(i)))
reversedStatusName = docStatusNames.get(i);
}
StringBuilder sql = new StringBuilder("SELECT u.AD_User_ID, l.created, c.columnName, l.oldValue, l.newValue, l.trxname ")
.append("FROM AD_ChangeLog l ")
.append("JOIN AD_Column c ON l.ad_column_id=c.ad_column_id ")
.append("JOIN AD_User u ON l.createdby=u.ad_user_id ")
.append("WHERE l.AD_Table_ID=? ")
.append("AND l.Record_ID=? ")
.append("ORDER BY l.created desc, l.trxName ");
PreparedStatement stmt = null;
ResultSet rs = null;
try {
stmt = DB.prepareStatement(sql.toString(), (String)null);
stmt.setInt(1, tableId);
stmt.setInt(2, recordId);
rs = stmt.executeQuery();
List<String> columns = null;
List<Pair<String, String>> changes = null;
String currentTrx = null;
String currentDocStatusOld = null;
String currentDocStatusNew = null;
Timestamp updated = null;
int userId = -1;
while (rs.next()) {
String columnName = rs.getString(3);
String trxName = rs.getString(6);
String oldValue = rs.getString(4);
String newValue = rs.getString(5);
if (columns == null) {
columns = new ArrayList<String>();
changes = new ArrayList<>();
}
if (currentTrx == null || currentTrx.equals(trxName)) {
if (currentTrx == null)
currentTrx = trxName;
if (columnName.equals("DocAction")) {
continue;
} else if (columnName.equals("DocStatus")) {
if (currentDocStatusNew == null)
currentDocStatusNew = newValue;
currentDocStatusOld = oldValue;
} else {
GridField field = gridTab.getField(columnName);
if (field != null && field.isDisplayed(true)) {
columns.add(field.getHeader());
changes.add(new Pair<String, String>(oldValue, newValue));
}
}
} else {
buildChangeLogMessage(gridTab, docActionValues,
docActionNames, reversedStatusName, columns,
currentDocStatusOld, currentDocStatusNew,
updated, userId, changes);
currentTrx = trxName;
currentDocStatusOld = null;
currentDocStatusNew = null;
columns = new ArrayList<String>();
changes = new ArrayList<>();
if (columnName.equals("DocAction")) {
continue;
} else if (columnName.equals("DocStatus")) {
if (currentDocStatusOld == null)
currentDocStatusOld = oldValue;
currentDocStatusNew = newValue;
} else {
GridField field = gridTab.getField(columnName);
if (field != null && field.isDisplayed(true)) {
columns.add(field.getHeader());
changes.add(new Pair<String, String>(oldValue, newValue));
}
}
}
userId = rs.getInt(1);
updated = rs.getTimestamp(2);
}
buildChangeLogMessage(gridTab, docActionValues, docActionNames,
reversedStatusName, columns, currentDocStatusOld,
currentDocStatusNew, updated, userId, changes);
if (gridTab != null && gridTab.getValue("CreatedBy") != null) {
MUser createdBy = MUser.get(Env.getCtx(), (int) gridTab.getValue("CreatedBy"));
StringBuilder sb = new StringBuilder(" ")
.append(Msg.getMsg(Env.getCtx(), "Created")).append(" ");
if (gridTab.getTabNo() == 0) {
sb.append(Env.getContext(Env.getCtx(), gridTab.getWindowNo(), "_WinInfo_WindowName", true));
} else {
sb.append(gridTab.getName());
}
buildActivityMessage((Timestamp) gridTab.getValue("Created"), sb.toString(), createdBy);
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
DB.close(rs, stmt);
}
}
public void buildChangeLogMessage(GridTab gridTab,
ArrayList<String> docActionValues,
ArrayList<String> docActionNames, String reversedStatusName,
List<String> columns, String currentDocStatusOld,
String currentDocStatusNew, Timestamp updated, int userId, List<Pair<String,String>> changes) {
if (currentDocStatusOld != null && currentDocStatusNew != null) {
buildDocActionMessage(docActionValues, docActionNames, reversedStatusName, updated, new MUser(Env.getCtx(), userId, (String)null),
currentDocStatusOld, currentDocStatusNew, gridTab.getWindowNo());
} else if (columns != null && columns.size() > 0) {
StringBuilder sb = new StringBuilder(" ");
sb.append(Msg.getMsg(Env.getCtx(), "Updated")).append(" ");
for(int i = 0; i < columns.size(); i++) {
if (i > 0) {
if (i+1 == columns.size()) {
sb.append(" ")
.append(Msg.getMsg(Env.getCtx(), "AND").toLowerCase())
.append(" ");
} else {
sb.append(", ");
}
}
Pair<String, String> change = changes.get(i);
sb.append("<i>")
.append(columns.get(i));
sb.append(" (")
.append(change.getX() != null && !"NULL".equals(change.getX())? change.getX() : "")
.append(" > ")
.append(change.getY() != null && !"NULL".equals(change.getY()) ? change.getY() : "")
.append(")");
sb.append("</i>");
}
buildActivityMessage(updated, sb.toString(), new MUser(Env.getCtx(), userId, (String)null));
}
}
private void buildDocActionMessage(ArrayList<String> docActionValues,
ArrayList<String> docActionNames, String reversedStatusName, Timestamp updated,
MUser user, String fromStatus, String toStatus, int windowNo) {
String docAction = null;
if (DocAction.STATUS_Completed.equals(toStatus)) {
docAction = DocAction.ACTION_Complete;
} else if (DocAction.STATUS_Approved.equals(toStatus)) {
docAction = DocAction.ACTION_Approve;
} else if (DocAction.STATUS_Voided.equals(toStatus)) {
docAction = DocAction.ACTION_Void;
} else if (DocAction.STATUS_Reversed.equals(toStatus)) {
docAction = DocAction.ACTION_Reverse_Accrual;
} else if (DocAction.STATUS_NotApproved.equals(toStatus)) {
docAction = DocAction.ACTION_Reject;
} else if (DocAction.STATUS_InProgress.equals(toStatus)) {
if (DocAction.STATUS_Completed.equals(fromStatus)) {
docAction = DocAction.ACTION_ReActivate;
} else if (DocAction.STATUS_Drafted.equals(fromStatus) || DocAction.STATUS_Invalid.equals(fromStatus)) {
docAction = DocAction.ACTION_Prepare;
}
} else if (DocAction.STATUS_Closed.equals(toStatus)) {
docAction = DocAction.ACTION_Close;
} else if (DocAction.STATUS_Invalid.equals(toStatus)) {
docAction = DocAction.ACTION_Invalidate;
}
if (docAction == null)
return;
String docActionName = null;
if (DocAction.ACTION_Reverse_Accrual.equals(docAction) || DocAction.ACTION_Reverse_Correct.equals(docAction)) {
docActionName = reversedStatusName;
} else {
for(int i = 0; i < docActionValues.size(); i++) {
if (docActionValues.get(i).equals(docAction)) {
docActionName = docActionNames.get(i);
break;
}
}
}
StringBuilder sb = new StringBuilder("<i>")
.append(docActionName).append("</i>")
.append(" ").append(Env.getContext(Env.getCtx(), windowNo, "_WinInfo_WindowName", true));
buildActivityMessage(updated, sb.toString(), user);
}
private void buildActivityMessage(Timestamp activityDate, String activityMessage, MUser user) {
SimpleDateFormat dateFormat = DisplayType.getDateFormat(DisplayType.DateTime);
StringBuilder sb = new StringBuilder();
sb.append("<div class=\"help-content\">\n");
sb.append("<strong>").append(user.getName()).append("</strong> ").append(activityMessage);
sb.append("<div>&nbsp;</div><div>").append(dateFormat.format(activityDate)).append("</div>");
sb.append("</div>");
Hbox hlayout = new Hbox();
hlayout.setHflex("1");
String hlayoutClass= "activity-card";
if (getChildren().size() > 0)
hlayoutClass = hlayoutClass + " activity-card-spacing";
hlayout.setSclass(hlayoutClass);
appendChild(hlayout);
Html contents = new Html();
contents.setContent(sb.toString());
hlayout.appendChild(contents);
}
}

View File

@ -63,9 +63,12 @@ import org.zkoss.zul.Borderlayout;
import org.zkoss.zul.Center;
import org.zkoss.zul.Div;
import org.zkoss.zul.Hbox;
import org.zkoss.zul.Hlayout;
import org.zkoss.zul.Listhead;
import org.zkoss.zul.Listheader;
import org.zkoss.zul.North;
import org.zkoss.zul.Radio;
import org.zkoss.zul.Radiogroup;
import org.zkoss.zul.South;
/**
@ -92,15 +95,16 @@ public class WRecordInfo extends Window implements EventListener<Event>
* Record Info
* @param title title
* @param dse data status event
* @param gridTab
*/
public WRecordInfo (String title, DataStatusEvent dse)
public WRecordInfo (String title, DataStatusEvent dse, GridTab gridTab)
{
super ();
this.setTitle(title);
if (!ThemeManager.isUseCSSForWindowSize())
{
ZKUpdateUtil.setWindowWidthX(this, 500);
ZKUpdateUtil.setWindowHeightX(this, 400);
ZKUpdateUtil.setWindowWidthX(this, 800);
ZKUpdateUtil.setWindowHeightX(this, 600);
}
else
{
@ -118,7 +122,7 @@ public class WRecordInfo extends Window implements EventListener<Event>
try
{
init ( dynInit(dse, title) );
init ( dynInit(gridTab, dse, title) );
}
catch (Exception e)
{
@ -130,6 +134,7 @@ public class WRecordInfo extends Window implements EventListener<Event>
private Listbox table = new Listbox();
private RecordTimeLinePanel timeLinePanel = new RecordTimeLinePanel();
private ConfirmPanel confirmPanel = new ConfirmPanel (false);
/** Logger */
@ -169,7 +174,7 @@ public class WRecordInfo extends Window implements EventListener<Event>
Pre pre = new Pre();
Text text = new Text(m_info.toString());
text.setParent(pre);
pre.setParent(div);
pre.setParent(div);
//
Borderlayout layout = new Borderlayout();
@ -181,15 +186,41 @@ public class WRecordInfo extends Window implements EventListener<Event>
center.setParent(layout);
if (showTable)
{
table.setSclass("record-info-changelog-table");
ZKUpdateUtil.setHflex(table, "true");
ZKUpdateUtil.setVflex(table, "true");
North north = new North();
north.setParent(layout);
north.appendChild(div);
north.appendChild(div);
center.appendChild(table);
ZKUpdateUtil.setWidth(table, "100%");
ZKUpdateUtil.setVflex(table, true);
Radiogroup group = new Radiogroup();
div.appendChild(group);
Hlayout hlayout = new Hlayout();
hlayout.setSclass("record-info-radiogroup");
Radio radio = new Radio(Msg.getElement(Env.getCtx(), "AD_ChangeLog_ID"));
radio.setRadiogroup(group);
hlayout.appendChild(radio);
radio = new Radio(Msg.getMsg(Env.getCtx(), "TimeLine"));
radio.setRadiogroup(group);
hlayout.appendChild(radio);
div.appendChild(hlayout);
group.setSelectedIndex(0);
group.addEventListener(Events.ON_CHECK, evt -> {
int index = group.getSelectedIndex();
if (index == 0) {
if (table.getParent() == null && timeLinePanel.getParent() != null) {
timeLinePanel.detach();
center.appendChild(table);
}
} else if (index == 1) {
if (table.getParent() != null && timeLinePanel.getParent() == null) {
table.detach();
center.appendChild(timeLinePanel);
}
}
});
}
else
{
@ -197,6 +228,7 @@ public class WRecordInfo extends Window implements EventListener<Event>
ZKUpdateUtil.setVflex(div, "true");
center.appendChild(div);
}
//
South south = new South();
south.setSclass("dialog-footer");
@ -219,11 +251,12 @@ public class WRecordInfo extends Window implements EventListener<Event>
/**
* Dynamic Init
* @param gridTab
* @param dse data status event
* @param title title
* @return true if table initialized
*/
private boolean dynInit(DataStatusEvent dse, String title)
private boolean dynInit(GridTab gridTab, DataStatusEvent dse, String title)
{
if (dse.CreatedBy == null)
return false;
@ -250,10 +283,14 @@ public class WRecordInfo extends Window implements EventListener<Event>
//get uuid
GridTable gridTable = null;
String tabName = null;
if (dse.getSource() instanceof GridTab)
if (gridTab != null)
{
GridTab gridTab = (GridTab) dse.getSource();
gridTable = gridTab.getTableModel();
}
else if (dse.getSource() instanceof GridTab)
{
gridTab = (GridTab) dse.getSource();
gridTable = gridTab.getTableModel();
tabName = gridTab.getName();
}
else if (dse.getSource() instanceof GridTable)
@ -300,6 +337,10 @@ public class WRecordInfo extends Window implements EventListener<Event>
m_permalink.setVisible(po.get_KeyColumns().length == 1);
}
}
if (gridTab != null)
{
timeLinePanel.render(gridTab);
}
// Title
if (tabName == null && dse.AD_Table_ID != 0)

View File

@ -0,0 +1,53 @@
/***********************************************************************
* This file is part of iDempiere ERP Open Source *
* http://www.idempiere.org *
* *
* Copyright (C) Contributors *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License *
* as published by the Free Software Foundation; either version 2 *
* of the License, or (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the Free Software *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, *
* MA 02110-1301, USA. *
* *
* Contributors: *
* - hengsin *
**********************************************************************/
package org.adempiere.webui.window;
import org.adempiere.webui.component.Window;
import org.compiere.model.GridTab;
/**
* @author hengsin
*
*/
public class WRecordTimeLine extends Window {
/**
* generated serial id
*/
private static final long serialVersionUID = -7887774385203335178L;
private RecordTimeLinePanel activityLayout;
/**
*
*/
public WRecordTimeLine() {
activityLayout = new RecordTimeLinePanel();
appendChild(activityLayout);
}
public void render(GridTab gridTab) {
activityLayout.render(gridTab);
}
}

View File

@ -277,3 +277,27 @@
height: 80% !important;
}
}
.activity-card {
border: 1px solid #d0cdc8;
border-top-left-radius: 2px;
border-top-right-radius: 2px;
border-bottom: 2px solid #d0cdc8;
border-bottom-left-radius: 4px;
border-bottom-right-radius: 4px;
margin-left: 3px;
margin-right: 3px;
}
.activity-card-spacing {
margin-top: 8px;
}
.activity-card .help-content {
font-size: 13px;
}
.record-info-dialog .record-info-radiogroup {
padding: 4px 4px 8px 4px;
}
.record-info-dialog .record-info-changelog-table {
margin-left: 3px;
margin-right: 3px;
}

View File

@ -89,15 +89,15 @@
}
.record-info-dialog {
width: 500px;
height: 400px;
width: 800px;
height: 600px;
}
@media screen and (max-width: 500px) {
@media screen and (max-width: 800px) {
.record-info-dialog {
width: 100%;
}
}
@media screen and (max-height: 400px) {
@media screen and (max-height: 600px) {
.record-info-dialog {
height: 100%;
}