{
#!/bin/bash
# ============================================================
# quickpush.sh
# Jalanin langsung di folder project kamu.
# Edit 2 variable di bawah ini:
# ============================================================
CUSTOM_REPO_NAME:""
COMMIT_MSG="Quick Push to Repo"
GIT_EMAIL="276874584+pakcli@users.noreply.github.com"
# ============================================================
# (jangan edit di bawah ini)
# ============================================================
BRANCH="main"
info() { echo ""; echo " ➜ $1"; }
ok() { echo " ✅ $1"; }
fail() { echo ""; echo " ❌ $1"; echo ""; echo " Tekan ENTER untuk keluar..."; read -r; exit 1; }
divider() { echo ""; echo " ────────────────────────────────────────"; }
clear
echo ""
echo " ╔══════════════════════════════════════╗"
echo " ║ QUICKPUSH TO GITHUB ║"
echo " ╚══════════════════════════════════════╝"
# ── STEP 1 — cek dir ─────────────────────────────────────────
divider
echo " [1/5] Checking current directory..."
REPO_NAME=$(basename "$PWD")
ok "Working dir: $PWD"
ok "Repo name : $REPO_NAME"
# ── STEP 2 — cek internet ────────────────────────────────────
divider
echo " [2/5] Checking internet connection..."
if ! curl -s --max-time 5 https://github.com > /dev/null 2>&1; then
fail "No internet. Check your network and try again."
fi
ok "Internet OK — GitHub reachable."
# ── STEP 3 — cek tools + autentikasi ─────────────────────────
divider
echo " [3/5] Checking tools & GitHub authentication..."
if ! command -v git &>/dev/null; then
fail "git not installed. Download: https://git-scm.com/"
fi
ok "git found: $(git --version)"
if ! command -v gh &>/dev/null; then
fail "GitHub CLI not installed. Download: https://cli.github.com/"
fi
ok "gh found: $(gh --version | head -1)"
if gh auth status &>/dev/null 2>&1; then
GH_USER=$(gh api user -q .login 2>/dev/null)
ok "Already authenticated as: $GH_USER"
else
info "Not logged in. Opening browser for GitHub login..."
echo ""
gh auth login --web --hostname github.com --git-protocol https
if ! gh auth status &>/dev/null 2>&1; then
fail "Authentication failed. Please try again."
fi
GH_USER=$(gh api user -q .login 2>/dev/null)
ok "Authenticated as: $GH_USER"
fi
# ── STEP 4 — init, commit, push ──────────────────────────────
divider
echo " [4/5] Pushing to GitHub..."
# Init repo jika belum ada
if [ ! -d ".git" ]; then
info "Initializing git repo..."
git init -q
git checkout -q -b "$BRANCH" 2>/dev/null || true
ok "Git repo initialized."
else
ok "Git repo already exists."
CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "")
if [ "$CURRENT_BRANCH" != "$BRANCH" ]; then
info "Switching to branch $BRANCH..."
git checkout -q "$BRANCH" 2>/dev/null || git checkout -q -b "$BRANCH"
fi
fi
# Set git identity pakai noreply email (anti GH007)
GH_NAME=$(gh api user -q .name 2>/dev/null || echo "$GH_USER")
git config user.email "$GIT_EMAIL"
git config user.name "$GH_NAME"
ok "Git identity: $GH_NAME <$GIT_EMAIL>"
# Stage semua file
info "Staging all files..."
git add -A
ok "Files staged."
# Commit
info "Committing: \"$COMMIT_MSG\""
if git diff --cached --quiet; then
ok "Nothing new to commit — already up to date."
else
git commit -q -m "$COMMIT_MSG"
ok "Committed."
fi
# Rewrite semua commit history pakai noreply email (fix GH007)
info "Rewriting commit history with safe email..."
git filter-branch -f --env-filter \
"GIT_AUTHOR_EMAIL='$GIT_EMAIL'
GIT_COMMITTER_EMAIL='$GIT_EMAIL'
GIT_AUTHOR_NAME='$GH_NAME'
GIT_COMMITTER_NAME='$GH_NAME'" \
--tag-name-filter cat -- --branches --tags 2>/dev/null
ok "Commit history rewritten."
# Cek remote
REMOTE_URL=$(git remote get-url origin 2>/dev/null || echo "")
if [ -z "$REMOTE_URL" ]; then
info "Setting up GitHub remote..."
if gh repo view "$GH_USER/$REPO_NAME" &>/dev/null 2>&1; then
ok "Repo already exists on GitHub. Adding remote..."
git remote add origin "https://github.com/$GH_USER/$REPO_NAME.git"
else
info "Creating new GitHub repo: $REPO_NAME ..."
gh repo create "$REPO_NAME" --public --source=. --remote=origin
ok "GitHub repo created."
fi
else
ok "Remote already set: $REMOTE_URL"
fi
# Force push
info "Pushing to origin/$BRANCH ..."
git push -u origin "$BRANCH" --force 2>&1 | sed 's/^/ /'
ok "Push complete."
# ── STEP 5 — enable GitHub Pages ─────────────────────────────
divider
echo " [5/5] Enabling GitHub Pages..."
HTTP_STATUS=$(gh api \
--method POST \
-H "Accept: application/vnd.github+json" \
"/repos/$GH_USER/$REPO_NAME/pages" \
-f "source[branch]=$BRANCH" \
-f "source[path]=/" \
--silent \
-w "%{http_code}" \
2>/dev/null || echo "err")
if [ "$HTTP_STATUS" = "201" ]; then
ok "GitHub Pages enabled."
elif [ "$HTTP_STATUS" = "409" ] || [ "$HTTP_STATUS" = "422" ]; then
ok "GitHub Pages already enabled."
else
gh api \
--method PUT \
-H "Accept: application/vnd.github+json" \
"/repos/$GH_USER/$REPO_NAME/pages" \
-f "source[branch]=$BRANCH" \
-f "source[path]=/" > /dev/null 2>&1 \
&& ok "GitHub Pages configured." \
|| echo " ⚠️ Pages: enable manually at https://github.com/$GH_USER/$REPO_NAME/settings/pages"
fi
# ── DONE ─────────────────────────────────────────────────────
echo ""
echo " ╔══════════════════════════════════════╗"
echo " ║ ALL DONE! 🎉 ║"
echo " ╚══════════════════════════════════════╝"
echo ""
echo " 🔗 GitHub Repo : https://github.com/$GH_USER/$REPO_NAME"
echo " 🌐 GitHub Pages : https://$GH_USER.github.io/$REPO_NAME"
echo ""
echo " ⏳ Pages URL live dalam ~1–2 menit."
echo ""
echo " Tekan ENTER untuk keluar..."
read -r
}
CH="main" ; info() { echo ""; echo " ➜ $1"; } ; ok() { echo " ✅ $1"; } ; fail() { echo ""; echo " ❌ $1"; echo ""; echo " Tekan ENTER untuk keluar..."; read -r; exit
<div class="profile-sidebar">
<div class="profile-sidebar-header">
<div class="sidebar-header-two-columns">
<!-- Column 1: Toggles (Left Column) -->
<div id="sidebar-header-actions" class="header-col-left">
<span class="sidebar-toggle-label">Toggle:</span>
<button id="btn-toggle-stats" class="btn-add btn-stats-toggle" title="Toggle Statistics">📊
Stats</button>
<button id="btn-toggle-sheet-sidebar" class="btn-add" title="Toggle Spreadsheet Editor" style="display: none;">📋 Sheet Editor</button>
</div>
<!-- Column 2: Device Name & Add Member (Right Column) -->
<div class="header-col-right">
<div id="device-identity-widget" class="device-identity-widget" style="display: flex; align-items: center; gap: 8px; background: rgba(140, 98, 57, 0.05); border: 1px solid var(--panel-border); padding: 8px 12px; border-radius: 8px; font-size: 0.82rem; color: var(--text-main); width: 100%; box-sizing: border-box; justify-content: flex-start; margin-bottom: 4px;">
<span>📱</span>
<button id="btn-edit-device-name" title="Change name" style="background: transparent; border: none; color: #a855f7; cursor: pointer; font-size: 0.85rem; padding: 2px; display: inline-flex; align-items: center; justify-content: center; outline: none; transition: transform 0.2s;" data-has-listener="true">✏️</button>
<span id="widget-device-name" style="font-weight: 600;">Host PC (Admin)</span>
</div>
<button id="btn-add-member-camera" class="btn-add btn-camera-mode" title="Add Member" style="white-space: nowrap; width: 100%;">+ Add Member</button>
</div>
</div>
</div>
<!-- Stats -->
<div class="section stats-grid collapsed">
<div class="stat-card">
<span class="stat-value" id="stat-total">30</span>
<span class="stat-label">Total Members</span>
</div>
<div class="stat-card">
<span class="stat-value" id="stat-gens">4</span>
<span class="stat-label">Generations</span>
</div>
<div class="stat-card">
<span class="stat-value text-blue" id="stat-males">20</span>
<span class="stat-label">Males</span>
</div>
<div class="stat-card">
<span class="stat-value text-pink" id="stat-females">10</span>
<span class="stat-label">Females</span>
</div>
</div>
<!-- Interactive details panel -->
<div class="section details-panel">
<div id="sidebar-form-placement-target" style="display: none; width: 100%; padding-bottom: 20px;"></div>
<div class="empty-detail-state" id="empty-detail-state" style="display: none;">
<div class="info-icon">👤</div>
<p>Click on any family member inside the tree or search above to load their profile details and
relationships.</p>
</div>
<div class="detail-card" id="detail-card" style="display: block;">
<div class="profile-header">
<div class="profile-avatar-container">
<img id="detail-avatar" src="https://cdn.balkan.app/shared/f11.png" alt="" style="display: block;">
<div id="detail-initial-avatar" class="initial-avatar" style="display: none;"></div>
</div>
<div class="profile-meta" style="flex-grow: 1;">
<div style="display: flex; justify-content: space-between; align-items: flex-start; gap: 8px;">
<h3 id="detail-name">Prince Andrew</h3>
<button id="btn-edit-member" class="btn-edit-icon" title="Edit Profile">✏️</button>
</div>
<p id="detail-nickname" class="title">Duke of York</p>
<span id="detail-gender-badge" class="gender-badge badge-female">female</span>
</div>
</div>
<!-- Personal Details -->
<div class="relationship-group">
<h4>Personal Details</h4>
<div class="personal-details-grid">
<div class="detail-row">
<span class="detail-label">Born:</span>
<span id="detail-birth" class="detail-value">Unknown</span>
</div>
<div class="detail-row" id="detail-death-row" style="display: none;">
<span class="detail-label">Died:</span>
<span id="detail-death" class="detail-value"></span>
</div>
</div>
</div>
<!-- Majors -->
<div class="relationship-group">
<h4>Majors (Jurusan)</h4>
<div id="detail-jurusan" class="relation-list"><span class="relation-tag no-link">None</span></div>
</div>
<!-- Photo History -->
<div class="relationship-group" id="detail-photos-group" style="display: block;">
<h4>Photo History</h4>
<div id="detail-photos-list" class="photos-history-list"><div class="photo-history-item-row"><input type="date" class="photo-date-input"><img src="https://cdn.balkan.app/shared/f11.png" title="Click to preview"><div class="photo-actions"><button class="btn-replace-photo" title="Replace this specific photo">✏️</button><button class="btn-expand-photo" title="View fullscreen">⛶</button><button class="btn-delete-photo-row" title="Delete Photo">✕</button></div></div></div>
</div>
<!-- Parents -->
<div class="relationship-group">
<h4>Parents</h4>
<div class="parent-links">
<div class="relation-item">
<span class="relation-label">Father:</span>
<span id="detail-father"><span class="relation-tag" onclick="selectAndCenterNode(4)">👑 Prince Philip</span></span>
</div>
<div class="relation-item">
<span class="relation-label">Mother:</span>
<span id="detail-mother"><span class="relation-tag" onclick="selectAndCenterNode(3)">👑 Queen Elizabeth II</span></span>
</div>
</div>
</div>
<!-- Spouses -->
<div class="relationship-group">
<h4>Spouses / Partners</h4>
<div id="detail-spouses" class="relation-list"><span class="relation-tag no-link">None</span></div>
</div>
<!-- Children -->
<div class="relationship-group">
<h4>Children</h4>
<div id="detail-children" class="relation-list"><span class="relation-tag no-link">None</span></div>
</div>
</div>
<!-- Edit Form Panel (initially hidden) -->
<div class="edit-panel" id="edit-panel" style="display: none;">
<h3 id="edit-form-title">Edit Profile</h3>
<form id="member-form" onsubmit="return false;">
<div class="form-group">
<label for="form-name">Name</label>
<input type="text" id="form-name" placeholder="Full Name" required="">
</div>
<div class="form-group flex-group">
<div class="form-half">
<label for="form-nickname">Nickname</label>
<input type="text" id="form-nickname" placeholder="Nickname">
</div>
<div class="form-half">
<label for="form-gender">Gender</label>
<select id="form-gender" required="">
<option value="male">Male</option>
<option value="female">Female</option>
</select>
</div>
</div>
<div class="form-group flex-group">
<div class="form-half">
<label for="form-birth">Date of Birth</label>
<input type="date" id="form-birth">
</div>
<div class="form-half">
<label for="form-death">Date of Death</label>
<input type="date" id="form-death">
</div>
</div>
<div class="form-group flex-group">
<div class="form-half">
<label for="form-jurusan">Majors (Comma-separated)</label>
<input type="text" id="form-jurusan" placeholder="e.g., Math, Physics">
</div>
</div>
<div class="form-group">
<label for="form-img">Profile Image URL</label>
<input type="url" id="form-img" placeholder="https://domain.com/photo.png">
<span class="form-helper">💡 Tip: You can paste an image directly (Ctrl+V) here or anywhere
on this panel!</span>
</div>
<div class="form-group flex-group">
<div class="form-half">
<label for="form-father">Father</label>
<select id="form-father">
<option value="">Unknown / None</option>
</select>
</div>
<div class="form-half">
<label for="form-mother">Mother</label>
<select id="form-mother">
<option value="">Unknown / None</option>
</select>
</div>
</div>
<div class="form-group">
<label>Spouses / Partners</label>
<div id="form-spouses-list" class="spouses-checkbox-list">
<!-- Dynamically populated list of checkboxes -->
</div>
</div>
<div class="form-actions">
<button type="button" id="btn-save-member" class="btn btn-save">💾 Save Changes</button>
<button type="button" id="btn-cancel-edit" class="btn btn-cancel">❌ Cancel</button>
</div>
<div class="form-danger-actions" id="delete-action-container">
<button type="button" id="btn-delete-member" class="btn btn-delete">🗑️ Delete
Member</button>
</div>
</form>
</div>
</div>
</div>
di broser inspect
@media (max-width: 900px) {
body.sheet-open:not(.sheet-fullscreen) .profile-sidebar:not(.collapsed) {
1. [ ] bottom: 290px !important;
}
}
@media (max-width: 900px) {
body.sheet-open:not(.sheet-fullscreen):not(.profile-sidebar-collapsed) .tree-container {
2. [ ] height: calc(100% - 600px) !important;
3. [ ] margin-bottom: 0 !important;
}
}
di css sourcecode
/* When BOTH sidebar and sheet are open (not fullscreen) */
body.sheet-open:not(.sheet-fullscreen):not(.profile-sidebar-collapsed) .tree-container {
height: calc(100% - 680px) !important;
height: calc(100% - 600px) !important;
margin-bottom: 0 !important;
}
/* Stack sidebar above sheet when both are open on mobile */
body.sheet-open:not(.sheet-fullscreen) .profile-sidebar:not(.collapsed) {
bottom: 360px !important;
bottom: 290px !important;
}